UUID生成ツール

    v4は122ビットの乱数です。予測不可能で、IDから何も推測させたくない場合に適しています。順序がランダムなため、データベースの主キーには向きません。

    入力した内容が保存・記録・共有されることはありません。

    ブラウザ内でUUIDを生成します。ランダムなv4と時系列ソート可能なv7を選べます。必要な数を指定し、個別または一括でコピーできます。

    使い方

    1. ランダムならv4、時系列ソートが必要ならv7を選びます。
    2. 生成する個数を選びます。
    3. 個別にコピーするか、「すべてコピー」を使います。

    128ビットに入っているもの

    UUIDは16バイトで、おなじみの8-4-4-4-12の区切りで32桁の16進数として書かれます。すべてのビットが自由に使えるわけではなく、4ビットがバージョンを、さらに2〜3ビットがバリアントを示します。そのため、ここで生成されるUUIDの13桁目は必ず4か7で、17桁目は常に8、9、a、bのいずれかになります。

    バージョン4のUUIDでは、残りの122ビットがランダムに埋められます。バージョン7は先頭48ビットをミリ秒単位のタイムスタンプに使い、残りの74ビットをランダムに埋めることで、予測困難性を少し犠牲にして順序性を得ています。

    ハイフンにも大文字・小文字にも情報はありません。ただしRFC 9562では、出力は小文字で行い、入力はどちらでも受け付けるよう定めています。urn:uuid:という接頭辞や、Windowsのツールが好む波括弧は表示上のもので、中身ではありません。

    v7が生まれた理由

    バージョン4のUUIDは一様にランダムです。何も明かすべきでない識別子にはまさに理想的ですが、主キーにはまさに望ましくない性質です。

    Bツリーインデックスはエントリを並べ替えた順序で保持します。ランダムなキーを挿入すると、その順序のどこにでも入り込むため、挿入のたびに別のページに触れることになり、作業に必要なページがメモリに収まらなくなると、すべての挿入が書き込みだけでなく読み込みも伴うようになります。ページは不均一に埋まって分割され、インデックスは対象のデータより大きくなり、テーブルが大きくなるにつれて書き込みのスループットが落ちていきます。連番のキーなら挿入は末尾に入り、よく使われるページは1つで済み、こうした問題は起きません。

    バージョン7は上位ビットにタイムスタンプを置くので、後で生成したIDは先に生成したIDより後ろに並びます。これにより、グローバルな一意性を保ち、マシン間の調整も不要なまま、その局所性を取り戻せます。RFC 9562でv7が追加されたのはこのためで、現在はキー列の既定として適切な選択です。

    トレードオフは現実にあります。v7のUUIDはおおよその作成日時を明かし、連続したIDは順序とおおよその生成ペースを示してしまいます。識別子が公開され、そのどちらかが機微な情報にあたる場合は、今でもv4が正しい選択です。

    衝突について

    2つのバージョン4 UUIDが衝突するのは、122ビットのランダム値が一致したときです。この数字を実感しやすくする一般的な方法が誕生日問題による見積もりです。1秒間に約10億個のUUIDを約85年間生成し続けて、ようやく集合のどこかで1件でも衝突が起きる確率が50%に達します。

    つまり実際には起きません。現実に起こりうる失敗は確率の問題ではありません。同じスナップショットから複製した仮想マシンや、起動時にエントロピーのない組み込み機器など、乱数生成器の初期化が不適切な場合。列に収めるためにUUIDを切り詰め、ランダム性の大半を失わせるコード。暗号学的でない乱数源に黙ってフォールバックするライブラリ。ここでのビットはブラウザの暗号学的乱数生成器から得ています。ほかの場面では、ライブラリが実際に何を使っているかを思い込まずに確認する習慣が役立ちます。

    保存方法

    テキスト形式は36文字ですが、値そのものは16バイトです。UUIDをchar(36)で保存すると容量は2倍以上になり、すべてのセカンダリインデックスが主キーのコピーを持つデータベースでは、そのコストがスキーマ全体に掛け算で効いてきます。

    PostgreSQLには16バイトで保存し、入力ではテキスト形式を受け付けるネイティブのuuid型があります。MySQLにはそのような型がないため、binary(16)を使い、アプリケーションとの境界で変換するのが一般的です。バージョン1のUUIDでは、時刻部分のバイトを並べ替えてソート可能にするよく知られた技がありますが、バージョン7ならその必要はありません。

    URLにUUIDを表示していて長さが気になる場合は、値を短くするのではなく、同じ16バイトをbase32やbase64urlでエンコードしてください。切り詰めは、うまくいかなくなるその瞬間まで、うまくいっているように見えるものです。

    バージョンの全体像

    バージョン1は60ビットのタイムスタンプとマシンのMACアドレスを埋め込みます。そのおかげでソート可能かつ追跡可能になりますが、後者の性質こそが敬遠されるようになった理由です。どのマシンが識別子を生成したかが漏れてしまうからです。バージョン2はDCEのセキュリティ版で、使っている人はほとんどいません。バージョン3とバージョン5は決定的です。名前空間と名前をそれぞれMD5とSHA-1でハッシュ化するため、同じ入力からは常に同じ識別子が得られます。

    バージョン4は純粋な乱数で、調整も時計もマシン固有の情報も不要なことから、どこでも既定の選択肢になりました。バージョン6はバージョン1を並べ替えてタイムスタンプが正しくソートされるようにしたもので、バージョン7が現代的な答えです。上位ビットにミリ秒単位のタイムスタンプ、続いて乱数という構成で、2024年にRFC 9562で標準化されました。

    バージョン5はもっと使われてよい存在です。すでに手元にあるもの、たとえばURLやファイルパス、テナント名から安定した識別子を導きたいとき、それをハッシュ化してUUIDにすれば、対応表も調整の手順もなしに、どのシステムでも同じ識別子が得られます。

    代替手段と、それが向いている場面

    ULIDはタイムスタンプと乱数をCrockford base32の26文字にまとめたものです。ソート可能で、大文字小文字を区別せず、テキスト表現はUUIDより短くなります。バージョン7より前から存在し、同じ問題を解決しているので、新しいシステムならたいていv7を使って標準の内側にとどまれば十分です。

    もともとTwitter発のSnowflake識別子は、タイムスタンプ、マシンID、シーケンス番号を64ビットに詰め込みます。コンパクトでソート可能、調整も最小限で済みますが、その代わりにノードごとに異なるIDを割り当てる必要があります。また、JavaScriptの数値が正確に保持できる範囲を超えるため、これを使うAPIは文字列で返します。

    nanoidはURL向けの短いランダム文字列を生成し、アルファベットも設定できます。既定の21文字で、v4 UUIDとほぼ同等のエントロピーを半分の文字数に収めます。そしてごく普通の自動採番の整数も、依然として多くのシステムで最良の選択です。最小で、インデックスも最速で、識別子が内部にとどまる限りまったく問題ありません。

    識別子を公開すると何が見えてしまうか

    公開URLに連番の整数を使うと、取引量が誰にでも伝わり、範囲を順に辿られてしまいます。/orders/1001 があるということは /orders/1000 も存在するということです。これが認可チェックの欠落と組み合わさると、安全でない直接オブジェクト参照と呼ばれる脆弱性になります。これは今でもWebアプリケーションで最も多く見つかる欠陥のひとつです。

    ランダムなv4 UUIDは列挙を防ぎますが、防げるのはそれだけです。アクセス制御ではありません。誰にも推測できない識別子であっても、入手した人なら誰でも使える識別子であることに変わりはなく、URLはリファラヘッダー、ブラウザの履歴、ログ、スクリーンショット、共有リンクを通じて漏れていきます。リクエストごとに、このユーザーがこのリソースを見てよいのかを必ず確認する必要があります。

    バージョン7はその不透明さを少しだけ手放します。レコードがいつごろ作られたかが分かり、連続する識別子からは順序と発行ペースが読み取れます。ある週に何人の顧客が登録したかを推測できてしまう程度には十分です。公開する識別子でそのどちらかが機微な情報になるなら、内部の主キーにはv7を使い、外部にはv4か別途用意したスラッグを見せるようにしてください。

    主キー、インデックス、ストレージ

    B-treeインデックスはエントリをソートされた状態で保持するため、ランダムなキーは挿入をインデックス全体に散らばらせます。インデックスがメモリに収まらなくなると、挿入のたびに書き込みだけでなく読み込みも発生し、ページは不均等に埋まって分割され、テーブルが大きくなるにつれて書き込みスループットが落ちていきます。連番のキーなら末尾に追記され、ホットなページはひとつだけで済み、これらの問題をすべて回避できます。これこそがバージョン7を使う理由のすべてです。

    ストレージも重要です。UUIDは16バイトですが、そのテキスト表現は36文字あります。PostgreSQLにはネイティブのuuid型があり、バイナリ形式で保存しつつ入力としてテキストも受け付けます。MySQLにはそうした型がないため、BINARY(16)にしてアプリケーションの境界で変換するのが慣例です。代わりにテキストで保存すると容量は2倍以上になり、すべてのセカンダリインデックスが主キーのコピーを持つエンジンでは、そのコストがスキーマ全体に波及します。

    よく採られる折衷案は、内部の結合には小さな整数の主キーを使い、外部向けの識別子としてUUIDにユニークインデックスを張るというものです。物理的なレイアウトを引き締めたまま、外の世界には中身の分からないハンドルを渡せます。

    コードで安全に生成する

    手作りで組み立てず、プラットフォームの関数を使ってください。ブラウザとNodeではcrypto.randomUUID、Pythonではuuid4、JavaではUUID.randomUUIDです。これらは暗号論的な生成器から値を取るので、衝突についての議論が成り立つのもそのおかげです。Math.randomにフォールバックするライブラリ、実際に一部の古いJavaScriptパッケージがそうでしたが、そうしたものは見た目こそ同じでも予測可能な識別子を生み出します。

    現実に起きる失敗は統計的なものではありません。同じスナップショットから複製された仮想マシンは同じエントロピープールで起動することがありますし、組み込み機器は起動直後にエントロピーがほとんどないこともあります。また、カラムに収めるためにUUIDを切り詰めるコードは、ランダム性の大半を壊しておきながら、見た目だけは識別子らしいものを残してしまいます。

    あとは二つの習慣で防げます。識別子の比較は大文字小文字を区別せずに行ってください。RFC 9562は小文字で出力しつつどちらも受け入れよと定めているからです。そしてWindowsのツールが付ける波括弧やurn:uuid:という接頭辞は取り除きましょう。さらに、形だけを信用せず入力時に検証してください。URLに含まれるUUIDらしき文字列も、結局はユーザー入力だからです。

    よくある質問

    UUID v4とv7の違いは何ですか?
    v4は122ビットの乱数のみで構成され、何も推測させず予測もできません。v7は先頭に48ビットのミリ秒タイムスタンプを持ち、その後に乱数が続くため、後から生成したIDは必ず後ろに並びます。v4は生成時刻を隠し、v7は時刻が分かる代わりにソートできます。
    データベースの主キーにはどちらを使うべきですか?
    v7です。ランダムなv4キーはインデックス全体に分散して挿入されるため、B-treeのページが断片化し、書き込み性能とキャッシュ効率が低下します。v7キーは順序どおりに追加されるため、グローバルな一意性を保ちつつ連番に近い挙動になります。
    暗号学的に安全な乱数ですか?
    はい。Math.randomではなく、ブラウザの暗号学的乱数生成器であるcrypto.getRandomValuesを使用しています。v4は122ビットの乱数を持つため、衝突は現実的な問題になりません。
    なぜv1やv5がないのですか?
    v1はMACアドレスとクロック情報を含むためホストの特定につながり、RFC 9562も新規システムにはv7を推奨しています。v3とv5は名前ベースで、名前空間と名前から決定的にIDを生成するため専用の入力が必要であり、別のツールとして提供すべきものです。
    UUIDはサーバーで生成されますか?
    いいえ。ブラウザ内で生成され送信されないため、当サイトを含め誰も生成されたUUIDを知ることはありません。
    UUIDとGUIDの違いは何ですか?
    実質的に同じものです。GUIDは同じ128ビット識別子に対するMicrosoftの呼称で、Windows系のツールでは波かっこ付きで表示されることがあります。その形式が必要な場合は「波かっこ」オプションを使用してください。

    最終更新:

    問題を報告

    操作手順、期待した結果、実際の結果を記載してください。秘密情報や個人情報は削除してください。

    関連ツール