UUID Version 7 (時刻順序付き)

RFC 9562で定義されたUUID v7を解説。ミリ秒精度のタイムスタンプにより時刻順にソート可能で、データベースに最適な新世代UUIDの仕組みと利点を紹介します。

Version

詳細な説明

UUID Version 7はRFC 9562(2024年5月)で導入された時刻順序付きUUIDで、UUID v4に内在するデータベースインデックスの問題を解決するために設計されました。最上位ビットにミリ秒精度のUnixタイムスタンプを埋め込むことで、v7 UUIDは生成時刻順に辞書順ソートが可能です。

ビットレイアウト(128ビット):

  • ビット0-47(48ビット):エポックからのUnixタイムスタンプ(ミリ秒)
  • ビット48-51(4ビット):バージョン 0111(7)
  • ビット52-63(12ビット):サブミリ秒精度またはランダムデータ
  • ビット64-65(2ビット):バリアント 10
  • ビット66-127(62ビット):ランダムデータ

タイムスタンプが先頭の48ビットを占めるため、16進数表現はタイムスタンプのプレフィックスで始まります。例えば、2024年1月15日に生成されたUUID v7は 018d1a2b-3c4d-7e5f-a6b7-c8d9e0f1a2b3 のようになります。先頭の 018d... がミリ秒タイムスタンプをエンコードしています。

時刻順序がデータベースで重要な理由: PostgreSQL、MySQL、その他多くのデータベースで使用されるB-treeインデックスは、単調増加するキーで最も高いパフォーマンスを発揮します。近い時刻に生成されたUUID v7の値はインデックス内で近接して格納されるため、ランダム書き込みではなくシーケンシャル書き込みになります。ベンチマークでは、インデックス付きカラムにおけるINSERTスループットがUUID v4と比較して一貫して2〜10倍改善しています。

サブミリ秒の順序付け: 同一ミリ秒内に複数のUUIDが生成された場合、バージョンフィールドの後の12ビットにはモノトニックカウンターまたはランダム値を使用できます。カウンター方式はミリ秒内の厳密な順序を保証し、ランダム方式は確率的な順序を提供します。

生成例:

function uuidv7() {
  const timestamp = Date.now();
  const bytes = new Uint8Array(16);
  // Set 48-bit timestamp
  bytes[0] = (timestamp / 2**40) & 0xff;
  bytes[1] = (timestamp / 2**32) & 0xff;
  bytes[2] = (timestamp / 2**24) & 0xff;
  bytes[3] = (timestamp / 2**16) & 0xff;
  bytes[4] = (timestamp / 2**8) & 0xff;
  bytes[5] = timestamp & 0xff;
  // Fill remaining with random
  crypto.getRandomValues(bytes.subarray(6));
  bytes[6] = (bytes[6] & 0x0f) | 0x70; // version 7
  bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 10
  const hex = [...bytes].map(b => b.toString(16).padStart(2, '0')).join('');
  return [hex.slice(0,8), hex.slice(8,12), hex.slice(12,16), hex.slice(16,20), hex.slice(20)].join('-');
}

トレードオフ: UUID v7は生成時刻が漏洩するため、セキュリティ上の配慮が必要なコンテキストでは望ましくない場合があります。48ビットのタイムスタンプは西暦10889年までサポートしているため、Y2K問題のような懸念はありません。

ユースケース

UUID v7は新規アプリケーションにおけるデータベース主キーに最適な選択肢です。特に、一意性と時系列順序の両方が必要な分散システムで、効率的なクエリやページネーションに威力を発揮します。

試してみる — UUID Generator

フルツールを開く