Universally Unique Identifier (UUID) は、レコードを識別するために使用される 16 バイトの値です。UUID の詳細については、Wikipedia を参照してください。
UUIDv4 や UUIDv7 など、異なる UUID バリアントがありますが (こちらを参照) 、ClickHouse は、挿入された UUID が特定のバリアントに準拠しているかどうかを検証しません。
UUID は内部的には 16 個のランダムなバイト列として扱われ、SQL レベルでは 8-4-4-4-12 の表記形式が使用されます。
UUID 値の例:
61f0c404-5cb3-11e7-907b-a6006ad3dba0
デフォルトの UUID は全ゼロです。これは、たとえば新しいレコードが挿入される際に UUID カラムの値が指定されていない場合に使用されます。
00000000-0000-0000-0000-000000000000
歴史的な理由により、UUID は後半部分でソートされます。これは UUIDv4 の値では問題ありませんが、プライマリインデックスの定義に UUIDv7 カラムを使用すると、パフォーマンスが低下する可能性があります (ソートキーやパーティションキーでの使用は問題ありません) 。
より具体的には、UUIDv7 の値は前半にタイムスタンプ、後半にカウンタを持ちます。
そのため、スパースなプライマリインデックスにおける UUIDv7 のソート (つまり各インデックスグラニュールの先頭の値) は、カウンターフィールドに基づいて行われます。
UUID が前半部分 (タイムスタンプ) でソートされると仮定すると、クエリの冒頭にあるプライマリインデックスの索引解析ステップでは、1 つを除くすべてのパーツのすべての mark を絞り込めると期待されます。
しかし、後半部分 (カウンタ) でソートされる場合は、すべてのパーツについて少なくとも 1 つの mark が返されることが見込まれ、その結果、不要なディスクアクセスが発生します。
例:
CREATE TABLE tab (uuid UUID) ENGINE = MergeTree PRIMARY KEY (uuid);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
SELECT * FROM tab;
┌─uuid─────────────────────────────────┐
│ 019d2555-7874-7e9d-a284-9b45a0b2f165 │
│ 019d2555-7874-7e9d-a284-9b46c3353be7 │
│ 019d2555-7878-77fc-a36f-4081aa58ec2b │
│ 019d2555-7878-77fc-a36f-40826555fb9b │
│ 019d2555-7870-7432-ba62-5250ac595328 │
│ 019d2555-7870-7432-ba62-5251da22bd19 │
│ 019d2555-786c-73e9-a031-4a7936df7d56 │
│ 019d2555-786c-73e9-a031-4a7a35a9544f │
│ 019d2555-7868-7333-89d1-2bd1639899c3 │
│ 019d2555-7868-7333-89d1-2bd297eb7d42 │
└──────────────────────────────────────┘
回避策として、UUID は後半部分から抽出したタイムスタンプに変換できます。
CREATE TABLE tab (uuid UUID) ENGINE = MergeTree PRIMARY KEY (UUIDv7ToDateTime(uuid));
-- または代わりに: [...] PRIMARY KEY (toStartOfHour(UUIDv7ToDateTime(uuid)));
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
INSERT INTO tab SELECT generateUUIDv7() FROM numbers(2);
SELECT * FROM tab;
結果 (同じデータが挿入された場合) :
┌─uuid─────────────────────────────────┐
│ 019d2555-7868-7333-89d1-2bd1639899c3 │
│ 019d2555-7868-7333-89d1-2bd297eb7d42 │
│ 019d2555-786c-73e9-a031-4a7936df7d56 │
│ 019d2555-786c-73e9-a031-4a7a35a9544f │
│ 019d2555-7870-7432-ba62-5250ac595328 │
│ 019d2555-7870-7432-ba62-5251da22bd19 │
│ 019d2555-7874-7e9d-a284-9b45a0b2f165 │
│ 019d2555-7874-7e9d-a284-9b46c3353be7 │
│ 019d2555-7878-77fc-a36f-4081aa58ec2b │
│ 019d2555-7878-77fc-a36f-40826555fb9b │
└──────────────────────────────────────┘
ORDER BY (UUIDv7ToDateTime(uuid), uuid)
ClickHouse には、ランダムなバージョン4のUUID値を生成する generateUUIDv4 関数があります。
例 1
この例では、UUID カラムを持つテーブルを作成し、そのテーブルに値を挿入する方法を説明します。
CREATE TABLE t_uuid (x UUID, y String) ENGINE=TinyLog
INSERT INTO t_uuid SELECT generateUUIDv4(), 'Example 1'
SELECT * FROM t_uuid
┌────────────────────────────────────x─┬─y─────────┐
│ 417ddc5d-e556-4d27-95dd-a34d84e46a50 │ Example 1 │
└──────────────────────────────────────┴───────────┘
例 2
この例では、レコードの挿入時に UUID カラムの値を指定していないため、デフォルトの UUID 値が挿入されます。
INSERT INTO t_uuid (y) VALUES ('Example 2')
SELECT * FROM t_uuid
┌────────────────────────────────────x─┬─y─────────┐
│ 417ddc5d-e556-4d27-95dd-a34d84e46a50 │ Example 1 │
│ 00000000-0000-0000-0000-000000000000 │ Example 2 │
└──────────────────────────────────────┴───────────┘
UUID データ型でサポートされているのは、String データ型でもサポートされている関数のみです (たとえば、min、max、count など) 。
UUID データ型は、算術演算 (たとえば、abs) や、sum や avg などの集約関数には対応していません。