UUID v4 vs v7: IDにはどのバージョンを使うべきか

ランダムなv4 UUIDと時刻順に並ぶv7 UUIDは、異なる問題を解決します。なぜv7がたいてい優れた主キーになるのか、v4が今でも適切なのはどんなときか、そしてバージョンビットが実際に何を意味するのかを説明します。

新しい行、新しいファイル、あるいはキューに載せるメッセージに識別子が必要に なります。まず手が伸びるのはUUIDです。128ビットで、中央のカウンターなしに どこでも生成でき、ほぼ確実に一意です。しかし「UUID」が一つのものを指して いるわけではありません。どのバージョンを選ぶかによって、値の並び方、 データベースのキーとしての振る舞い、そして何が漏れるかが変わります。 2026年に重要なのはv4(ランダム)とv7(時刻順)の二つで、両者は互換的では ありません。

UUIDとは実際に何か

UUIDは128ビットで、通常は8-4-4-4-12の区切りで32桁の16進数として 書きます: 0190b8e4-...。このうち6ビットは固定のオーバーヘッドです。 4ビットがバージョン(どの生成方式が作ったか)を、2ビットがバリアント (レイアウト標準)を表します。残りの122ビットがバージョンごとに異なる部分で、 ランダムなノイズ、タイムスタンプ、あるいはその混合です。UUIDを読むとき、 バージョンの数字は13番目の16進文字です。そこが4ならv4、7ならv7です。

0190b8e4-5e3a-7c2d-9f10-3a4b5c6d7e8f
                ^ version (7)

その一桁がすべてです。以下の内容はすべて、残りのビットを何が埋めるのか、 そしてそれがなぜ重要なのかについてです。

v4: ランダム

v4 UUIDは、バージョンとバリアントのビットが刻まれた122ビットのランダム値 です。構造も、タイムスタンプも、マシン識別子もありません。暗号学的に安全な ソースから得たエントロピーだけです。これによって二つの強力な性質が生まれます。 何の調整もなしにどこでも一つ生成でき、直前の値から次の値を推測できません。

コストはv4をデータベースの主キーとして使うときに現れます。ほとんどの データベースは主キーをB-treeに格納し、これがディスク上で行をキー順に保ちます。 ランダムなキーはランダムな位置に落ちるため、挿入がインデックス全体に書き込みを 散らし、ページ分割を起こし、バッファキャッシュをかき乱します。負荷の高い テーブルでは、これはスループットとストレージに測定可能な損失となります。 インデックスは断片化し、整列されたものより速く肥大化します。数千行であれば 気づくことはありませんが、数百万件の挿入になると気づくことになります。

ですからv4は、推測できないことが要点であるときに正しい選択です。URLに 入れるパスワードリセットトークン、列挙されたくない公開オブジェクトID、APIキー がそれにあたります。書き込みの多い主キーの既定値としては誤った選択です。

v1と時刻をめぐる回り道

v7以前、時刻ベースの選択肢はv1でした。60ビットのタイムスタンプにマシンの MACアドレスを加えたものです。二つの点がv1を沈めました。MACアドレスはどの マシンがそのIDを作ったかを漏らし、タイムスタンプのバイトがかき混ぜられた順序で 格納されるため、v1の値は文字列としてもバイトとしても時刻順に並びません。v1は 「時刻をエンコードする」という問題は解きましたが、「並べ替え可能である」という 問題は解かず、人々が実際に求めていた性質は後者でした。v1はレガシーとして扱えば よいでしょう。

v7: 時刻順

v7 UUIDは48ビットのUnixタイムスタンプをミリ秒単位で先頭に置き、残りを ランダム値で埋めます。タイムスタンプが最上位の部分でビッグエンディアンで 格納されるため、v7の値は生のバイト順で時刻順に並びます。したがって文字列形式 でも時刻順に並びます。順番に生成された二つのIDは、その順番で並びます。

この一つの変更がB-treeの問題を解決します。新しいキーは古いキーより大きいので、 挿入は散らばらずにインデックスの右端付近に追加されます。v4の「どこでも生成、 調整不要」という性質を保ちながら(後続のランダムビットが、同じミリ秒内の衝突を 依然として無視できるほど低くします)、インデックスの局所性と無料の時刻順の 並びを得られます。主キーにおいては、この組み合わせこそがv7が既定の推奨になった 理由です。

v4 v7
内容 122ランダムビット 48ビットmsタイムスタンプ + ランダム
時刻順に並べ替え可能 いいえ はい (バイト順・文字列順)
PKとしてインデックスに優しい いいえ (書き込みを散らす) はい (ほぼ順次)
生成時刻を明かす いいえ はい
向いている用途 不透明・推測不能なトークン データベースの主キー

v7が引き受けるトレードオフ

v7が並べ替え可能なのは、まさに生成時刻を埋め込んでいるからであり、それが 同時に唯一の漏れでもあります。v7 UUIDを手にした誰もが、それがいつ生成された かをミリ秒単位まで読めます。主キーにとっては、たいてい無害でしばしば有用です。 タイミングを露出させたくない公開トークン、つまりクーポンコード、招待リンク、 列挙可能に近いものでは、その漏れはv4にとどまる理由になります。バージョンは、 そのIDが何にさらされるかを見て選ぶものであり、習慣で選ぶものではありません。

ULIDを見たことがあるなら、それはv7と同じ発想です。ミリ秒タイムスタンプの 接頭辞にランダム値を加えたもので、v7の標準化に先んじており、別のテキスト エンコーディング(Crockford base32、26文字)を使います。v7はRFCが認めた形式で、 通常のUUIDとして相互運用できるので、新規の作業ではULIDをそれより選ぶ理由は ほとんどありません。

読み取りと生成

UUIDを調べるときは、まずバージョンの数字を見て、それから残りが何を意味するかを 判断します。v7の先頭バイトはタイムスタンプにデコードされますが、v4のバイトは 何にもデコードされません。生成の面では、最新のランタイムが追いついてきています。 ブラウザとNodeのcrypto.randomUUID()は依然としてv4を出し、v7はライブラリ (npmのuuid、他のエコシステムのuuid6/uuid7)を通じて、そしてますます データベースで直接利用できます。私たちの UUIDジェネレーターは両方のバージョンをブラウザで 生成するので、依存関係を取り込まずにひとまとめでコピーできます。

どちらのバージョンもできない一つのことは、何かを認証することです。UUIDは 識別子であって、検証できる秘密ではありません。あなたが発行したことを証明する トークンが必要なら、それは署名であり、 JWT署名アルゴリズムで扱っています。 そしてUUIDはハッシュではありません。コンテンツをフィンガープリント化しようと UUIDに手を伸ばしているなら、 ハッシュ・暗号化・エンコーディング の区別こそがあなたに必要なものです。

要約すると、主キーや大量に保存するものにはv7に手を伸ばし、IDが公開され、 生成時刻が誰の関心事でもあってはならないときにはv4を使います。