UUID 生成ツール
UUID を一つずつ、または五十個まとめて生成します。ランダムな v4 と、先頭のタイムスタンプで作成順に並ぶためデータベースのキーに向く時刻順の v7 を選べます。
122 ビットのランダム。いつ作られたかは何も明かしません。
生成中…
UUID とは何か、なぜ存在するのか
UUID — Universally Unique Identifier、Microsoft の文書では GUID とも呼ばれます — は、なじみ深い 8-4-4-4-12 の区切りで 32 桁の 16 進数として書かれる 128 ビットの値です。その全目的は、別々のシステムが互いに調整せず独立に識別子を作り、それでも結果が衝突しないと確信できるようにすることです。データベースの自動採番列と違うのはそこです: 2 台のサーバー、飛行機の中でオフラインの 2 台のモバイル端末、そしてバックグラウンドジョブが、誰の許可も求めずに同じ瞬間にレコードを作れます。
ここでの一意性は保証ではなく確率的なものです。バージョン 4 は 122 ビットを乱数に委ね、これは何十億もの値を生んでも、繰り返しの確率がディスクが黙ってデータを破損する確率よりはるかに低いほど大きな空間です。実務では一意として扱えます。数学が弱点になることはありません。
バージョン 4 と 7 — 重要な選択
バージョン 4 は端から端までランダムです。いつ作られたか、誰が、どの順で、といった情報を一切運びません。それが最大の強みか中心的な欠点かは、どこに置くかで決まります。
バージョン 7 は 2024 年に RFC 9562 で標準化され、最初の 48 ビットをミリ秒の Unix タイムスタンプに置き換え、残りを乱数で埋めます。時刻が先に来て値が左から右へ読まれるため、v7 識別子を素のテキストとして整列させると、時系列にも整列します。
これは見た目だけの違いではありません。データベースは主キーを整列した B ツリーのインデックスに保ちます。v7 キーを挿入すると新しい行はツリーの右端、前の行の隣に着地します — 書き込むページはメモリに残り、インデックスはきれいに育ちます。v4 キーを挿入すると各書き込みがランダムな位置に着地するので、データベースは毎回別のページに触れ、キャッシュのヒット率が下がり、インデックスが断片化します。大きく忙しいテーブルでは挿入スループットの差が大きく、v7 が主キーにこれほど速く採用されたのはそのためです。
- データベースの主キー、イベントやログの識別子、作成時刻で整列や範囲検索したいものには v7 を選んでください。
- 識別子が信頼できない場所に現れ、何も漏らしてはならないとき — あるレコードが別のより少し前に作られたという事実さえも — には v4 を選んでください。
- 相関 ID・冪等キー・ファイル名など、順序が問題にならないところでは、どちらでも構いません。
引き換えになるのはまさにその漏れです: v7 識別子は、それを見る者に、作成された時刻をミリ秒単位で伝えます。行の ID にはたいてい無害でしばしば有用です。パスワードリセットのトークンや公開の共有リンクには、公開するつもりのなかった情報です — そしてそれらはそもそも UUID であるべきではなく、専用のシークレット生成器からのランダムなトークンであるべきです。
同じミリ秒内での順序
多くの v7 実装を捕らえる微妙な点: 現代のハードウェアは 1 ミリ秒に 1 つよりはるかに多くの識別子を生みます。2 つの値がタイムスタンプを共有すると、その順序は続く乱数ビットが決めます — つまりランダムに、です。密なループで 50 個生むと、おおまかにしか整列していない 50 個の値が得られ、v7 を選んだまさにその性質を失います。
RFC 9562 はこれを単調カウンターで扱い、このツールはそれを実装しています: タイムスタンプの直後の 12 ビットがミリ秒内で上へ数えるので、バッチはおおよそではなく厳密に増加します。カウンターが満杯になると — 同じミリ秒に 4096 個を超えると — 生成器は折り返して前より前に整列する値を出す代わりに、次のミリ秒を借ります。NTP 補正で起きる、時計が後ろに動く場合も同じように扱います。
v7 の値を左から右へ読むと、0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b を例に:
- 0190a1b2-c3d4 — 48 ビットのミリ秒 Unix タイムスタンプ。先に来るので、テキスト順が時刻順です。
- 7 — バージョンのニブルで、これを v4 ではなく v7 にするものです。
- e5f — 12 ビットの単調カウンターで、単一のミリ秒内で増えていきます。
- 8 — バリアントビットで、RFC 9562 がすべての現代の UUID に対して固定しています (常に 8、9、a、b のいずれか)。
- a9b-0c1d2e3f4a5b — 残りの 62 ビットで、純粋にランダムです。
UUID の保存と利用
正規の形は小文字でハイフン付きで、RFC 9562 は生成器がまさにそれを出すべきだと言っています — だからこのツールは書式の選択肢を提供しません。実際にはほかの綴りにも出会うでしょう: Microsoft のツールでは大文字で波括弧に包まれ、短い列にしたい誰かがハイフンを取り除いた形もあります。どれも同じ 128 ビットで、比較は大小を無視すべきです。
効いてくるのは保存です。UUID は 16 バイトですが、テキスト形式は 36 文字です — なので文字列として保存すると、行の中で、そしてより重要なのはそれを含むすべてのインデックスの中で、空間が倍以上になります。ネイティブ型があるならそれを使ってください:
uuid -- PostgreSQL: ネイティブの 16 バイト型 BINARY(16) -- MySQL: コンパクト。CHAR(36) は 20 バイト/行を無駄にする uniqueidentifier -- SQL Server crypto.randomUUID() // JavaScript: v4 のみ、セキュアコンテキストが必要 uuid.uuid4() / uuid7() # Python: 標準ライブラリの v4。v7 はライブラリ経由
最後の注意: UUID は識別するもので、認可するものではありません。推測できないため、それを含む未公開の URL を非公開扱いにしたくなります。しかし識別子は漏れます — ログ・ブラウザ履歴・リファラーヘッダー・スクリーンショットを通して — ので、本当に守る必要があるものには、その背後に本物の権限チェックが今も必要です。
その UUID が v4 か v7 かを見分ける
識別子はたいてい、どこから来たかの但し書きなしに渡されますが、但し書きは要りません: バージョンは値そのものの中に書かれています。どちらも同じ 8-4-4-4-12 の区切りの 32 桁の 16 進数なので、違いは形にはなく、決まった位置にある 2 桁の数字にあり、あとはそこから決まります。
- 3 つ目のグループの最初の桁がバージョンのニブルです。そこが 4 ならバージョン 4、7 ならバージョン 7 で、値の他のどの部分にも口出しはできません。
- 4 つ目のグループの最初の桁にはバリアントビットが載り、どちらのバージョンでも 8、9、a、b のいずれかです — つまり手元のどちらかを教えてはくれません。代わりに教えてくれるのは、その値がそもそも RFC 9562 に従っているということです。その位置がそれ以外なら、より古い配置か、UUID ではありません。
- バージョンのニブルが 7 なら、最初の 12 桁が作成時刻です: 1970 年の初めからのミリ秒を 16 進数で書いた 48 ビットの数です。
- 4 なら、それ以上読むものはありません。v4 は時刻も機械も順序も符号化しません。それこそが v4 を選ぶ理由の性質です。
上の節が読み解く値、0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b を取ってみましょう。3 つ目のグループが 7 で始まるのでバージョン 7 であり、最初の 12 桁は 0190a1b2c3d4 です — この 16 進数を 10 進数に直して Unix タイムスタンプ変換に渡せば、2024 年 7 月のある日が得られます。その隣で 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d は 3 つ目のグループが 4 で始まり、その最初の 12 桁は何にも復号されないランダムなデータです。二つが一致するのはちょうど一点だけ: どちらも 4 つ目のグループが、バリアントビットの許す 4 つの文字のいずれかで始まっています。
v7 は v4 と同じくらい衝突に強いのか
もっともな問いです。v7 の乱数は実際に少ないからです。バージョン 4 は 128 ビットのうち 122 ビットを乱数に使います。バージョン 7 は 48 ビットをタイムスタンプに、さらにここでは 12 ビットを単調カウンターに使うので、乱数は 62 ビットしか残りません — 半分を少し超える程度です。裸の数として読めば、これは深刻な後退に見えます。
- このツールの 1 つのバッチの中では、重複は起こりにくいのではなく起こり得ません。ミリ秒を共有する値は異なるカウンター値を受け取り、ミリ秒を共有しない値は異なるタイムスタンプを持つので、等しくなる 2 つは存在しません — 確率ではなく算術です。
- 同じ瞬間に生成している 2 台の機械のあいだでは、確率は最悪でも 62 ビット上の誕生日問題であり、それはおおよそ空間の平方根で五分になります: およそ 20 億個の値、しかもすべて同じ 1 ミリ秒の中で、です。
- 異なるミリ秒のあいだでは、v7 の衝突は起こりにくいどころか不可能です。先頭の桁そのものが違うからです。
ですから正直な比較は 122 ビット対 62 ビットではありません。システムの一生のあいだ何度も引かれ続ける 1 つのくじと、ミリ秒ごとに別々に開かれ、そのミリ秒が終われば捨てられるはるかに小さなくじとの比較です。後者の仕組みのほうが強く、v4 を選ぶ理由は比較の節が挙げるものから変わりません — 衝突ではなく、v7 が作られた時刻を声に出してしまうことです。
既存のテーブルを v4 から v7 に切り替える
v7 を選んだあとに続く問いは、すでにテーブルにある行をどうするかです。安心できるのは、列そのものは何も変えなくてよいという点です。どちらのバージョンも同じ 36 文字のテキスト形式の同じ 128 ビットなので、PostgreSQL の uuid 列も BINARY(16) も CHAR(36) も、混在に気づきもせずそれを保ちます。新しい行に v7 を生成し始めて、そこで終わりです。移行の手順も、遡っての埋め戻しもありません。
- すぐ手に入るもの: 今から挿入される行はどれも先頭にタイムスタンプを持つので、新しいキーはインデックス全体に散らばらず、その一端で互いの隣に着地します。この利点は新しい書き込みがどこへ行くかについてのもので、最初の挿入から効いています。
- 決して手に入らないもの: すでにある行は永久に順序を持ちません。与えられなかった作成時刻をあとから値に入れられるものは何もなく、識別子をすべて発行し直すというのは、それを指すすべての外部キーを書き換えるということです — 生成器を替えるよりはるかに大きな仕事で、インデックスのためだけに見合うことはまずありません。
- 注意すべきもの: 列の整列が一つのことを意味しなくなります。ミリ秒の数は 48 ビットの欄には小さな数なので、v7 のキーは範囲の下のほうの狭い帯に集まり、v4 のキーは範囲全体に広がって、ときおりその帯に紛れ込みます。
噛みついてくるのはこの最後の点です。キーで整列する問い合わせは新しいデータの上では正しく見え、古いデータについては黙って誤った報告をします。テーブル全体に一つの順序が要るなら、作成時刻の列を足してそれで並べ、識別子は識別子に戻してやってください。そのあいだも混在は少なくとも読み取れます: バージョンのニブルはどの値にも入っているので、必要なら問い合わせは二つ目の列なしに二つの時代を見分けられます。
よくある質問
- v4 と v7 のどちらを使うべきですか?
- データベースの主キーと、作成時刻で整列したいものには v7 を使ってください: 先頭のタイムスタンプが、挿入をインデックス全体に散らすのではなく末尾にまとめて保ちます。識別子がいつ作られたかも含め何も明かしてはならないときには v4 を使ってください。
- 2 つの UUID が同じになることはありますか?
- 可能ですが、消え入るほど起こりにくいです。バージョン 4 は 122 ビットの乱数を持つので、何十億もの値を生んだ後でも、衝突の確率はストレージが黙ってそれらを破損する確率よりはるかに低いままです。
- バージョン 1、3、5 はどうなったのですか?
- バージョン 1 はタイムスタンプとマシンの MAC アドレスを符号化し、ハードウェアの素性を漏らし、ブラウザではそもそも作れません。バージョン 3 と 5 は、名前空間と名前から MD5 または SHA-1 を使って UUID を決定論的に導きます — 同じ入力が常に同じ識別子を生む必要があるときに有用ですが、新しく生成するのとは別の仕事です。
- UUID はシークレットトークンとして使うほど安全ですか?
- v4 UUID は推測できませんが、v7 は作成時刻を公然と符号化し、どちらも認証情報として意図されていません。パスワードリセット・セッショントークン・共有リンクには、専用のランダムなシークレットを生成し、識別子が推測しにくいことに頼るのではなくサーバー側で権限を確認してください。
- 私の v7 バッチが他のツールで完全に整列していないのはなぜですか?
- 多くの実装が単調カウンターを省くからです。複数の値がミリ秒を共有すると、その順序は続く乱数ビットに委ねられます。このツールは RFC 9562 のカウンターを実装しているので、ここで生成したバッチは厳密に増加します。
- UUID をデータベースにどう保存すべきですか?
- ネイティブの 16 バイト型があるならそれで — PostgreSQL の uuid、SQL Server の uniqueidentifier、MySQL の BINARY(16) です。代わりに 36 文字のテキスト形式で保存すると、行の中と、その列を含むすべてのインデックスの中で、使う空間が倍以上になります。
- これらはあなたのサーバーで生成されるのですか?
- いいえ。プラットフォームの暗号論的乱数源である crypto.getRandomValues を使ってブラウザ内で生成されます — crypto.randomUUID が使うのと同じものです。どの値もどこにも送られず、ページを再読み込みするとまったく新しい一式が得られます。
- v7 の UUID がいつ作られたか分かりますか?
- はい、値そのもの以外は何も要りません。最初の 12 桁の 16 進数は 1970 年の初めからのミリ秒の数です: 10 進数に直し、その結果を Unix タイムスタンプ変換に渡してください。v4 にはそうした欄がないので、そこの同じ 12 桁はランダムで、何にも復号されません。
- v7 の乱数ビットは v4 より少ないのですか?
- はい — ここでは 62 で、v4 の 122 に対しています。タイムスタンプとカウンターが場所を取るからです。それで実際に衝突しやすくなるわけではありません: 2 つの v7 の値は同じミリ秒に作られたときにしか衝突しえないので、その 62 ビットはシステムの一生にわたってではなく 1 ミリ秒の中で使われますし、このツールの 1 つのバッチの中ではカウンターが重複を、起こりにくいどころか起こり得ないものにします。
- v4 と v7 の UUID を同じ列に置けますか?
- はい。同じテキスト形式の同じ 128 ビットなので、スキーマは何も変わらず移行もありません — 今から v7 を生成し、古い行はそのままにしておきます。注意すべきは整列だけです: 新しい行どうしは作成順に並びますが、古いランダムな行がその周りに散らばっているので、キーによる整列はテーブル全体の時刻順にはなりません。
- UUID のバージョン 6 と 8 は何ですか?
- RFC 9562 が v7 と並べて両方を定義しています。バージョン 6 はバージョン 1 のタイムスタンプの各欄を並べ替えて値が時系列に整列するようにしたもので、すでに v1 に縛られたシステムに向けられています — それ以外は v7 を使うべきだと RFC は述べています。バージョン 8 は独自の配置のために意図的に空けられた枠で、固定されるのはバージョンとバリアントのビットだけ、残りの 122 ビットは自分で決められます。このツールはどちらも生成しません。
関連するツール
- ランダムトークン生成
安全な鍵、シークレット、パスフレーズを生成。
- モックデータ生成
シード付きのテストデータ。チェックディジットもIBANも本当に正しい。
- QRコードジェネレーター
エンコードの判断がすべて見える。モード、バージョン、レベル、マスク。