デジタル道具屋
デジタル道具屋
記事一覧へ戻る
更新: 読了 約 5 分

UUID v4・v7の仕組みと、安全なID設計の考え方

#development

執筆: ありがっさまりょうた

デジタル道具屋を個人で開発・運用しています。記事は実装時の検証結果と公式仕様を照合して執筆しています。

本記事の執筆方針と編集ポリシーについてはAbout ページをご覧ください。

導入:ID衝突はなぜ起きるのか

「別々のユーザーのデータが同じIDを持ってしまい、データが混ざる」というトラブルの典型的な原因は、IDの採番方式にあります。 単一のデータベースが1, 2, 3, 4と順番に番号を振る連番ID(オートインクリメント)は、1台のサーバーで完結している間は問題になりません。 しかし、複数のサーバーやサービスが同時にデータを書き込む構成では、採番の調停が取れずに同じ番号が二重に発行されることがあります。 本記事では、連番IDのリスク、UUIDの仕組み(v4・v7)、DB主キーやURLで使う際の実践的な注意点を、RFC 9562の仕様をもとに整理します。

1. 連番IDのよくある事故と致命的なリスク

連番IDは小規模なシステムでは十分に機能しますが、分散環境やマイクロサービス構成では次のような問題を引き起こします。

連番IDで起きる代表的な事故
  • ID衝突:複数サーバーで同時にレコード作成すると同じIDが発行される
  • セキュリティリスク:URLに id=42 のような連番が見えると、id=43 を推測して他人のデータにアクセスされる
  • データ統合の困難:異なるDB間でデータを統合するとき、IDが衝突してどちらかを捨てることに
  • スケーラビリティの壁:シャーディングや非同期処理と相性が悪い

連番IDには、情報漏えいの側面もあります。たとえばECサイトの注文IDが連番であれば、 ある日の最初と最後の注文IDの差分から、1日の注文件数を外部から推測できてしまいます。 この種の「IDから業務量が推測できる」問題は、セキュリティ監査で指摘される代表的な項目です。

2. UUIDを活用した安全なID設計

UUID(Universally Unique Identifier)は128ビットの識別子です。 バージョン4では128ビットのうち122ビットが乱数で、中央で採番を管理しなくても衝突が実用上無視できるほど低い確率になるよう設計されています(RFC 9562)。 ただし厳密には衝突しないことが「保証」されるわけではなく、あくまで確率的な仕組みです。 具体的には、生成した個数を n とすると少なくとも1組が衝突する確率は近似的に n2 / 2123 であり、 10億個(n = 109)生成しても約 10-19 にとどまります。

UUIDの形式

UUID v4の例

550e8400-e29b-41d4-a716-446655440000

8-4-4-4-12の形式で、合計36文字(ハイフン含む)

上の例を見ると、3番目のグループの先頭が 4、4番目のグループの先頭が a になっています。 これは偶然ではなく、RFC 9562で定められた固定ビットです。

  • バージョン(4ビット):3番目のグループの先頭16進数1桁。v4なら 4、v7なら 7
  • バリアント(上位2ビット):4番目のグループの先頭16進数1桁の上位2ビットが 10。したがってこの桁は 8・9・a・b のいずれかになる
  • 残り122ビット:v4ではすべて乱数

ブラウザやNode.jsでは、標準APIの crypto.randomUUID() でv4 UUIDを生成できます。

const id = crypto.randomUUID();
console.log(id);
// 例: "4efcb53a-f10a-472f-89a5-bfd2516c628c"
//                    ^ 3番目のグループ先頭は必ず 4
//                         ^ 4番目のグループ先頭は 8/9/a/b のいずれか

v4とv7の使い分け

バージョン特徴使いどころ
v4完全ランダム一般的なID生成(最も普及)
v7タイムスタンプ+ランダムソート可能なID(DB主キーに最適)

v7は先頭48ビットにUnixエポックからのミリ秒タイムスタンプを持ち、その後ろにバージョン(4ビット)、乱数(12ビット)、バリアント(2ビット)、乱数(62ビット)が続きます。 先頭がタイムスタンプなので、生成時刻順に文字列としてもバイナリとしても並びます。

v4はシンプルですが、値が完全にランダムなため、DBの主キーに使うと新しいレコードがインデックスのあらゆる位置に挿入され、インデックスが断片化します。 v7であればタイムスタンプ順に並ぶため、新しいレコードは常にインデックスの末尾付近に追加され、INSERT性能が安定します。

当サイトのUUID生成ツールを使えば、v4やv7のUUIDをワンクリックで大量生成できます。 テストデータ作成やシステム設計の検証に活用してください。

3. 実装時に知っておきたいテクニカルTips

UUIDを実際のシステムに組み込む際に、あわせて押さえておきたいポイントをまとめます。

Tips 1: UUIDをDBの主キーにする際の注意点

UUIDはランダム性が高いため、B-Treeインデックスのページ分割が頻発し、書き込み性能が劣化します。 対策としては、UUID v7(時系列ソート可能)を使うか、UUIDをBINARY(16)として格納してストレージを節約する方法があります。 36文字の文字列(CHAR(36))で保存すると1件あたり36バイト必要ですが、BINARY(16)なら16バイトで済み、インデックスサイズも比例して小さくなります。

Tips 2: URLに使う場合はBase62エンコード

UUIDをそのままURLに使うと36文字と長くなりがちです。128ビットの値をBase62(0-9、A-Z、a-zの62文字)でエンコードすると最大22文字に短縮できます。 たとえば 550e8400-e29b-41d4-a716-446655440000 をBase62(数字→大文字→小文字の順のアルファベット)でエンコードすると 2aUyqjCzEIiEcYMKj7TZtw(22文字)になります。 ハイフンや記号を含まないため、URLでの見た目もすっきりします。

注意点:UUIDの大文字・小文字

UUIDの仕様(RFC 9562)では、16進数の a〜f は小文字で出力し、入力時は大文字・小文字を区別しないと定められています。 しかし、データベースやプログラミング言語によっては文字列として比較するため、550E8400-... と 550e8400-... が別の値として扱われることがあります。 保存・比較の前に「常に小文字に正規化する」というルールを設けることで、比較時のバグを防げます。

IDの設計はシステムの土台であり、運用開始後に変更するのは非常に困難です。 分散環境での衝突回避、推測されにくさ、DBインデックスとの相性を最初の設計段階で検討し、要件に応じてv4とv7を選択することが重要です。 実際のフォーマットは、UUID生成ツールで生成したサンプルを見ながら確認すると理解が早まります。

参考にした一次情報

本記事の内容は、以下の公式仕様や一次情報を参照して執筆しています。

この記事の内容を実際に試す

解説した内容は、以下のツールでブラウザ上から無料で試せます。