テストとQA · 6 分で読めます

UUID v4 vs v7 in 2026: ビットレイアウト、ソート、インデックスコスト

UUID v4 vs v7 をビット単位で解説: 122 ビットのランダム値と 48 ビットのミリ秒タイムスタンプ、ソート挙動、B-tree インデックスコスト、プライバシー、エンコードサイズ。

著者: Charlie Simmon ·

UUID v4 と UUID v7 はどちらも 128 ビットの識別子ですが、共通点はそこまでです。v4 は 122 ビットをランダム性に費やします。v7 は先頭に 48 ビットの Unix ミリ秒タイムスタンプを置き、続いて 4 ビットのバージョン、74 ビットのランダム値、2 ビットのバリアントを配置します。バージョン番号は優劣の指標ではありません。識別子が何を漏らしてよいか、どのようにソートされるかを示す宣言です。

どちらも 2024 年に RFC 4122 を置き換えた RFC 9562 で定義されています。実用上の選択は 3 つの問いに集約されます。識別子を作成時刻でソートする必要があるか、B-tree の主キーに格納されるか、値を保持する誰もが作成時刻を読めることを許容できるか。本ガイドは経験則ではなくバイトレイアウトでこれらに答えます。

128 ビットのレイアウトを並べて比較

UUID はすべて 16 バイトで、各フィールドはテキスト上のハイフンではなく位置で定義されます。バイト 6 の上位ニブルがバージョン、バイト 8 の上位 2 ビットがバリアントです。それ以外はバージョンによって異なります。正規文字列では、13 番目の 16 進文字がバージョン、17 番目がバリアントです。

v4 では、バージョンとバリアント以外の 122 ビットがランダムです。v7 では、先頭 48 ビットがビッグエンディアンの Unix ミリ秒タイムスタンプであり、そのため v7 の値は生バイトとして作成順にソートされます。残りの 74 ビットは既定でランダムですが、RFC 9562 はミリ秒内の単調性のためにその一部をカウンタとして使うことを許容しています。

フィールドビットv4v7
バイト 0-548ランダムunix_ts_ms、ビッグエンディアン
バイト 6 上位ニブル40100 (バージョン 4)0111 (バージョン 7)
バイト 6 下位ニブルとバイト 712ランダムrand_a
バイト 8 上位ビット210x バリアント10x バリアント
バイト 8-15 の残り62ランダムrand_b
ランダムビット合計12212274

v4 の詳細

v4 UUID は 6 ビットが固定された乱数です。バイト 6 の上位ニブルは 0100 なので、正規文字列の 13 番目の文字は常に 4 です。バイト 8 の上位 2 ビットは 10 なので、17 番目の文字は常に 8、9、a、b のいずれかです。予測できるのはこれらだけで、残りの 30 桁の 16 進数字はランダムです。

このランダム性がセキュリティ特性です。v4 の値にはタイムスタンプ、シーケンス、マシン識別子が含まれないため、いつどこで作成されたかについて何も明かしません。そのため、列挙されてはならない公開識別子に適した選択肢となります。ただし、暗号学的に安全な乱数源で生成されることが前提です。122 ビットの空間では衝突は無視できるほど小さく、10 億個の値でも衝突確率はおよそ 2^-63 です。

  • バージョンニブル: 正規文字列の 13 番目は常に 4
  • バリアント: 17 番目は常に 8、9、a、b のいずれか
  • ランダムビット: 122、CSPRNG から生成
  • 意味のある順序を持たず、順序はランダム
  • 作成時刻もシーケンスも明かさない

v7 の詳細

v7 UUID は時刻順です。バイト 0 から 5 に Unix エポックからのミリ秒数をビッグエンディアンで格納します。バイト 6 の上位ニブルは 0111 です。バイト 6 の下位ニブルとバイト 7 全体が rand_a を形成し、バイト 8 の上位 2 ビットがバリアント、残りの 62 ビットが rand_b です。つまり 74 ビットのランダム値に加えて、目視で読めるタイムスタンプがあります。

タイムスタンプは 48 ビットで、西暦 10889 年までのミリ秒を表現できるため、オーバーフローは実用上の問題ではありません。同じミリ秒に作成された 2 つの v7 値はランダムビットまたはカウンタビットだけが異なり、時計が巻き戻らない限り、後から作成された値は常にバイト列として先の値より大きくなります。この順序性が要点です。

バイト内容例
0-5Unix ミリ秒、ビッグエンディアン0192f3c4a1b2
6 上位ニブルバージョン 77
6 下位ニブル + 7rand_a、12 ビットa1b
8 上位 2 ビットバリアント 108、9、a、b のいずれか
8 (下位 6) から 15rand_b、62 ビットランダムまたはカウンタ

ミリ秒内のソートと単調性

正規テキストの辞書順ソートはバイト順と一致します。ハイフンが固定位置にあり、小文字の 16 進数字は値と同じ順序でソートされるためです。テキスト形式でも 16 バイトでもソートすれば作成順になります。これはマシン間で調整なしに成り立つため、v7 はイベントログやソートキーの一部となる識別子に魅力的です。

問題は単一ミリ秒内です。既定では、同じミリ秒に作成された 2 つの値はランダムビットで順序付けられ、作成順にはなりません。RFC 9562 は、rand_a フィールドまたは左端のランダムビットを、タイムスタンプの刻みごとにランダムにシードしたカウンタとして使う方法を説明しています。この方法なら実用上ミリ秒内で単調になりますが、ランダム性が少し減り、共有カウンタを並行する生成側で慎重に扱う必要があります。

  • ミリ秒をまたぐ場合: 時計が単調である限り順序は保証される
  • ミリ秒内: カウンタを使わない限りランダム順
  • カウンタ方式: 刻みごとにランダムにシードし、値ごとにインクリメント
  • 時計の巻き戻し: 順序が壊れるため、単調な時計源を使う
  • 並行性: カウンタをアトミックにインクリメントしないと特性を失う

B-tree のインデックスコスト

ランダムな v4 主キーはインデックスのランダムな位置に挿入されます。B-tree ではほぼ毎回の挿入で異なるリーフページに触れ、バッファプールからページが追い出され、ページが中央で分割されて半分しか埋まらない状態になります。テーブルが大きくなるにつれてインデックスは肥大化し、書き込み増幅が増え、キャッシュヒット率が下がります。

v7 キーは右端付近に挿入されるため、ワーキングセットは小さく保たれ、ページ分割は安価な末尾で発生します。トレードオフは競合です。すべての書き込み側が同じ右端のページを求めるためです。単一データベースで毎秒数百書き込み程度なら通常は問題ありませんが、非常に高い並行性ではホットスポットになります。一般的な緩和策はバッチ処理、接続レベルのインターリーブ、上位ビットへの少量のジッタの付与です。

特性ランダムな v4 キー時刻順の v7 キー
挿入位置ランダムなリーフページ右端のリーフページ
ページ分割頻繁、ページ中央まれ、末尾
バッファプールの局所性大規模インデックスでは悪い良い
インデックスサイズ大きい、充填率が低いコンパクト
書き込みホットスポットツリー全体に分散右端に集中
時刻による範囲スキャン別カラムが必要ネイティブ

プライバシーとタイムスタンプの漏洩

v7 の値は、それを保持する誰にでも、作成されたおおよその時刻をミリ秒単位で示し、したがってその前後に何件のレコードが作成されたかも示します。内部キーやイベント ID には問題ありません。登録数、注文率、アカウントの経過時間を明かすべきでない公開識別子には適しません。

順序性と秘匿性の両方が必要な場合の選択肢は、v7 を内部に留めて別のランダムな公開 ID を公開するか、システム外に出るものには v4 を使うことです。どちらのバージョンでも UUID をケイパビリティトークンとして扱わないでください。どちらも識別子であり、認可ではありません。v4 の ID は実際上推測不可能ですが、それでもすべてのリクエストでアクセス制御に対して検証する必要があります。

  • v7 は作成時刻をミリ秒単位で明かす
  • 近傍の v7 値を列挙するとレコード量が明らかになる
  • v7 は内部に留め、v4 または別の公開 ID を公開する
  • どちらのバージョンもアクセストークンや秘密情報ではない
  • ランダム化された v7 というものはない。タイムスタンプは必須

ストレージとエンコードサイズ

ストレージの答えはどちらのバージョンも 16 バイトです。正規テキスト形式は 36 文字で、うち 32 文字が 16 進数字、4 文字がハイフンです。テキスト形式で保存するとサイズが倍になり比較も遅くなるため、ネイティブの UUID 型があればそれを使い、なければ BINARY(16) を使い、境界でのみフォーマットしてください。

SQL Server にはテストする価値のある注意点があります。uniqueidentifier 型は正規文字列とは異なるバイト順でソートされ、内部のバイト順も RFC 4122 と異なります。v7 の順序に依存するコードは、そのプラットフォームでソート挙動を前提とせず検証する必要があります。コンパクトなテキスト形式が必要な場合、パディングなしの base64url は 22 文字になりますが、標準の UUID 表現ではないため、正規形式を期待するツールを混乱させます。

表現サイズ備考
バイナリ16 バイトネイティブ保存、最速の比較
正規テキスト36 文字8-4-4-4-12、ハイフン付き
ハイフンなし 16 進32 文字一部の API やログで使用
base64url、パディングなし22 文字標準の UUID 形式ではない
Base3226 文字まれ、一部のチケットシステムで使用

選択、移行、テスト

識別子がデータベースキーである場合、2 つ目のカラムなしで作成時刻でソートしたい場合、または追記の多いテーブルを構築する場合は v7 を選びます。値が公開される場合、作成時刻が機密である場合、またはシステムがすでにタイムスタンプカラムを保存していて順序性から何も得られない場合は v4 を選びます。どちらも異なる制約に対する正しい選択であり、1 つのテーブルで混在させるのは通常は誤りです。

テストでは値ではなく構造を検証する必要があります。パース後にバージョンニブル、バリアントニブル、バイト長を確認します。v7 では、埋め込まれたタイムスタンプがテスト期間内に収まること、順に作成された 2 つの値が正しく比較されることを検証します。テストでリテラルの UUID をスナップショットしないでください。また、生成器がカウンタを使わない限り、v7 が前の値より厳密に大きいと検証しないでください。カウンタがなければ、同じミリ秒内の順序はランダムです。

状況推奨理由
大規模テーブルの主キーv7局所性と小さいインデックス
公開向け識別子v4タイムスタンプを開示しない
時刻でソートするイベントログv7カラムなしでネイティブな順序
セッションまたはリセットトークンどちらでもない専用のランダムトークンを使う
時刻カラムを持つ既存テーブルv4順序性はすでに利用可能
システム間の交換どちらでもよいどちらも標準の 128 ビット UUID

よくある質問

UUID v4 と v7 は同じサイズですか?+
はい。どちらも 128 ビット、16 バイト、正規テキストで 36 文字です。異なるのは内部レイアウトだけです。
UUID v7 は正しくソートされますか?+
ミリ秒をまたぐ場合は、生バイトとしても正規テキストとしても正しくソートされます。単一ミリ秒内では、生成器がカウンタを使わない限り既定の順序はランダムです。
データベースの主キーにはどちらが適していますか?+
v7 です。挿入が B-tree 全体に散らばらず右端付近に留まるためです。トレードオフは、非常に高い並行性で最終ページに書き込みホットスポットが生じることです。
UUID v7 は情報を漏洩しますか?+
作成時刻をミリ秒単位で明かし、近傍の値からレコード量も明らかになります。内部に留めるか、システム外に出る識別子には v4 を使ってください。
UUID をアクセストークンとして使えますか?+
いいえ。UUID は識別子です。v4 の値でもすべてのリクエストでアクセス制御に対して検証する必要があり、トークンは独自の有効期間ルールを持つ専用の生成器から発行すべきです。

例は合成データです。生成された住所を本人確認、アカウント登録、配送に使用しないでください。

参照標準

次に読む

すべてのガイド