web-dev-qa-db-ja.com

CoreDataでUUIDを効率的に挿入およびフェッチする方法

CoreDataにUUIDを保存および検索するための効率的な方法を探しています。これらのUUIDは、分散システム内の多くのiOSデバイスによって生成されます。これらの各デバイスは、約20〜50kのUUIDを格納できます。

UUIDを文字列としてCoreDataに保存すると、インデックス作成の効率が低下することは明らかです。しかし、一連の調査の結果、IDをコアデータにバイナリデータとして格納する(およびインデックスを付ける)ことは、文字列として格納するよりも効率が悪い可能性があることがわかりました

SQLitにはBINARYのようなまたはVARBINARYのようなデータ型がないため、サポートされています。 CoreDataのバイナリデータタイプのデータはすべてSQLitにBLOBとして格納されていると思います。 BLOBは、インデックス付けされるデータ型が最も遅い可能性があるため、パフォーマンスに悪影響を及ぼします。

それで、誰かが答えるのを手伝うことができます、コアデータにUUIDを保存するより効率的な方法はありますか?

20
Cable W

それらをASCII文字列として保存し、フィールドをインデックスにします。

[〜#〜]編集[〜#〜]

エガド、私はたまたま突っついていましたが、これに出くわしました。なんて恥ずべき答えでしょう。その日はちょっと気分が良かったに違いない。できれば、それを削除して次に進みます。ただし、それは不可能なので、アップデートの一部を提供します。

まず、何が「効率的」であるかを知る唯一の方法は、プログラムの時間とスペース、ソースコードの複雑さ、およびプログラマーの労力を考慮して測定することです。

幸いなことに、これは非常に簡単です。

私は非常に単純なOSXアプリケーションを作成しました。モデルは、identifierという単一の属性で構成されています。

属性をインデックスとしてマークしない場合、これは重要ではありません。ストアの作成にはかなり時間がかかりますが、クエリははるかに高速になります。

また、バイナリ属性の述語を作成することは、文字列の述語を作成することとまったく同じであることに注意してください。

fetchRequest.predicate =
    [NSPredicate predicateWithFormat:@"identifier == %@", identifier];

アプリケーションは非常に簡単です。まず、N個のオブジェクトを作成し、識別子属性にUUIDを割り当てます。 500オブジェクトごとにMOCを保存します。次に、すべての識別子を配列に格納し、ランダムにシャッフルします。次に、CDスタック全体が完全に破棄され、すべてがメモリから削除されます。

次に、スタックを再度構築し、識別子を繰り返し処理して、単純なフェッチを実行します。フェッチオブジェクトは、その1つのオブジェクトをフェッチするための単純な述語を使用して構築されます。これはすべて自動リリースプール内で行われ、各フェッチを可能な限り元の状態に保ちます(CDキャッシュとの相互作用があることを認めます)。さまざまな手法を比較しているだけなので、これはそれほど重要ではありません。

バイナリ識別子は、UUIDの16バイトです。

UUID文字列は[uuidUUIDString]を呼び出した結果の36バイトの文字列で、次のようになります(B85E91F3-4A0A-4ABB-A049-83B2A8E6085E)。

Base64文字列は24バイトの文字列であり、16バイトのUUIDバイナリデータをbase-64でエンコードした結果であり、同じUUIDに対して次のようになります(uF6R80oKSrugSYOyqOYIXg ==)。

カウントは、その実行のオブジェクトの数です。

SQLiteサイズは、実際のsqliteファイルのサイズです。

WALサイズは、WAL(ログ先行書き込み)ファイルの大きさです-参考までに...

Createは、保存を含め、データベースを作成する秒数です。

クエリは、各オブジェクトをクエリする秒数です。

Data Type     | Count (N) | SQLite Size | WAL Size  | Create  | Query
--------------+-----------+-------------+-----------+---------+---------
Binary        |   100,000 |   5,758,976 | 5,055,272 |  2.6013 |  9.2669
Binary        | 1,000,000 |  58,003,456 | 4,783,352 | 59.0179 | 96.1862
UUID String   |   100,000 |  10,481,664 | 4,148,872 |  3.6233 |  9.9160
UUID String   | 1,000,000 | 104,947,712 | 5,792,752 | 68.5746 | 93.7264
Base64 String |   100,000 |   7,741,440 | 5,603,232 |  3.0207 |  9.2446
Base64 String | 1,000,000 |  77,848,576 | 4,931,672 | 63.4510 | 94.5147

ここで最初に注意することは、実際のデータベースサイズは、格納されているバイト(1,600,000および16,000,000)よりもはるかに大きいことです。これはデータベースで予想されることです。追加のストレージの量は、実際のオブジェクトのサイズにいくらか比例します...これは識別子のみを格納するため、オーバーヘッドの割合が高くなります)。

次に、速度の問題については、参考までに、同じ1,000,000のオブジェクトクエリを実行しますが、フェッチでobject-idを使用するのに約82秒かかりました(それとexistingObjectWithID:error:の呼び出しとの大きな違いに注意してください。これにはなんと0.3065秒かかりました。 )。

実行中のコードでの機器の賢明な使用を含め、独自のデータベースのプロファイルを作成する必要があります。複数回実行した場合、数値は多少異なると思いますが、非常に近いため、この分析には必要ありません。

ただし、これらの数値に基づいて、コード実行の効率測定を見てみましょう。

  • 予想どおり、生のUUIDバイナリデータを格納すると、スペースの点でより効率的です。
  • 作成時間はかなり近いです(違いは、文字列を作成する時間と必要な追加のストレージスペースに基づいているようです)。
  • クエリ時間はほぼ同じように見えますが、バイナリ文字列は少し遅いように見えます。これが元々の懸念であったと思います-バイナリ属性でクエリを実行します。

バイナリはスペースを大幅に獲得し、作成時間とクエリ時間の両方に近い引き分けと見なすことができます。それらを考慮すると、バイナリデータを保存することが明らかに勝者です。

ソースコードの複雑さとプログラマーの時間はどうですか?

最新バージョンのiOSとOSXを使用している場合、特にNSUUIDの単純なカテゴリでは、実質的に違いはありません。

ただし、考慮事項が1つあります。それは、データベース内のデータの使いやすさです。バイナリデータを保存する場合、データを適切に視覚化することは困難です。

したがって、何らかの理由で、データベース内のデータを人間にとってより効率的な方法で保存したい場合は、文字列として保存することをお勧めします。したがって、base64エンコーディング(または他のエンコーディング-すでにbase-256エンコーディングになっていることを覚えておいてください)を検討することをお勧めします。

FWIW、NSDataとbase64文字列の両方としてUUIDに簡単にアクセスできるようにするカテゴリの例を次に示します。

- (NSData*)data
{
    uuid_t rawuuid;
    [self getUUIDBytes:rawuuid];
    return [NSData dataWithBytes:rawuuid length:sizeof(rawuuid)];
}

- (NSString*)base64String
{
    uuid_t rawuuid;
    [self getUUIDBytes:rawuuid];
    NSData *data = [NSData dataWithBytesNoCopy:rawuuid length:sizeof(rawuuid) freeWhenDone:NO];
    return [data base64EncodedStringWithOptions:0];
}

- (instancetype)initWithBase64String:(NSString*)string
{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:string options:0];
    if (data.length == sizeof(uuid_t)) {
        return [self initWithUUIDBytes:data.bytes];
    }
    return self = nil;
}

- (instancetype)initWithString:(NSString *)string
{
    if ((self = [self initWithUUIDString:string]) == nil) {
        self = [self initWithBase64String:string];
    }
    return self;
}
40
Jody Hagins