ID 体系の選び方:UUID v4 / v7・ULID・NanoID・Snowflake をユースケースで使い分ける
「テーブルの主キーを自動採番にするか、ULID にするか」「短縮 URL の ID をどう作るか」「複数サーバで衝突しない ID が要る」——いずれも ID 体系の選定です。本記事では現在現実的な 5 つの選択肢(UUID v4、UUID v7、ULID、NanoID、Snowflake)を、特性の比較と選定フローで整理します。
比較表
| 形式 | 長さ | エンコード | 時刻情報 | ソート可能 | 衝突確率 | 主なユースケース |
|---|---|---|---|---|---|---|
| UUID v4 | 36(hex) | hex + ハイフン | なし | 不可 | 122bit ランダム | 匿名性が要る ID、汎用 |
| UUID v7 | 36(hex) | hex + ハイフン | 48bit ms | 可 | 74bit ランダム | DB 主キー、ログ ID |
| ULID | 26(Crockford Base32) | Base32 | 48bit ms | 可 | 80bit ランダム | DB 主キー、URL に含めるID |
| NanoID | 21(任意) | URL-safe Base64 | なし | 不可 | 約 126bit ランダム | 短縮 URL、外部公開トークン |
| Snowflake | 19 桁の数値 | 64bit 整数 | 41bit ms | 可 | 22bit (worker+seq) | 数値主キーが必要な分散システム |
「時刻でソートしたいか」「長さの制約があるか」「数値カラムに入れたいか」が主な分岐点です。
UUID v4:匿名性とランダム性が最優先
crypto.randomUUID() で生成、122 bit のランダム。
- 生成順序の情報が一切漏れない
- どの言語・どの DB でもネイティブサポート
- データベースの B-tree インデックスではランダム性が裏目に出る:挿入順序がランダムなのでページ分割が頻発し、書き込みパフォーマンスが落ちる
UUID v4 をそのまま主キーにすると、書き込みの多いテーブルで顕著にスループットが落ちることがあります。「Postgres で UUID 主キーが遅い」という議論は大抵 v4 を指しています。
UUID v7:時刻順 + 標準仕様
RFC 9562(2024 年に発行)で正式化された新バージョン。
- 上位 48 bit がミリ秒タイムスタンプ
- 下位は 74 bit のランダム
- 文字列としても、バイナリとしてもソート可能
- v4 と同じ 36 文字フォーマットなので、
UUIDカラムをそのまま使える
DB 主キーとしての性能問題(ランダム挿入によるページ分割)が解決されており、新規プロジェクトの主キーは v7 が第一候補になりつつあります。クライアント側で生成しても、クライアントのクロックがそこそこ正しければ DB の挿入順は時刻順になります。
v4 → v7 への移行の罠
既存テーブルで UUID カラムが既に v4 の値で埋まっているところに v7 を混ぜてもソート性は復活しない。混在させるなら、新規行は v7、既存は v4 のまま、検索時には作成日時カラムを別途持つのが現実的。
ULID:UUID より短く、人にも読める
26 文字の Crockford Base32 で表現されます(例:01ARZ3NDEKTSV4RRFFQ69G5FAV)。
- 上位 48 bit ms タイムスタンプ + 下位 80 bit ランダム(仕様は UUID v7 と似ている)
- アルファベットが I/L/O/U を意図的に除外しており、人間が打ち間違えにくい
- URL に直接含められる(記号なし)
UUID v7 と思想は近いが、表現が違う。文字数は 36 vs 26 で ULID のほうが短い。一方、UUID v7 はバイナリとしての標準があり、DB の uuid 型が使える分、運用上の互換性は UUID 系が上。
ULID と UUID v7、結局どちらにすべきか
- すでに UUID 型が運用されている / 将来も DB のネイティブ UUID 型を使いたい → UUID v7
- ID を URL や CLI 引数に直接見せる、人がコピペすることがある → ULID
- バイナリで保存サイズを最小化したい(16 byte vs 任意バイト) → UUID v7
NanoID:短い・URL セーフ・カスタマイズ可能
デフォルト 21 文字、URL セーフな 64 文字アルファベット (A-Z a-z 0-9 _ -) のランダム生成。
- UUID とほぼ同等の衝突確率を 21 文字で達成
- 長さもアルファベットも変えられる(短くしたいなら 12 文字、人に読ませるなら大文字を抜く、など)
- 時刻情報は持たない
短縮 URL や外部公開する外向きのトークンに向きます。「UUID は長すぎる、でもセキュアな ID が欲しい」用途。
NanoID の長さは何文字あれば十分か
衝突確率は誕生日問題で計算できます。21 文字(126 bit のエントロピー)なら、毎秒 1000 個生成しても衝突するのに数十億年。実用上、外部公開 ID として 21 文字あれば足ります。逆に内部 ID であれば短くできる:1000 万件で衝突確率 0.0001% を狙うなら 12 文字程度。
Snowflake:64 bit 数値が要るとき
Twitter が公開した分散 ID 採番方式。
- 41 bit ミリ秒タイムスタンプ + 10 bit ワーカーID + 12 bit シーケンス = 64 bit
- 数値(
BIGINT)一発で済む - 時刻順にソート可能
Snowflake が向く状況
- 数値 ID が DB スキーマで要求されている(古いシステムとの互換、JOIN の高速化)
- 採番サーバを集中で持たず、ワーカーごとに自律的に採番したい
- ID をクライアントに見せても問題ない(時刻が漏れる)
Snowflake の罠
- ワーカーID の重複は致命的。複数ノードに同じ ID を割り当てると衝突が始まる
- 12 bit シーケンスは 1 ms あたり 4096 個まで。それを超える勢いで採番するワーカーは、シーケンスを使い切ったときに 1ms 待つ実装が標準
- クライアント側で生成する場合、ワーカーID を乱数にすると衝突が確率的に発生する。サーバー間で割り当てを協調できる環境向け
選定フロー
新規システムで ID 体系を決めるとき、以下の順で絞れます。
数値カラムにしか入れられない(古い RDB 互換、JOIN 性能要件)か?
- YES → Snowflake
- NO → 次へ
時刻でソートしたい(DB 主キー、ログ)か?
- YES → UUID v7(互換性重視)/ULID(短さ・可読性重視)
- NO → 次へ
生成元が秘密にしたい(生成時刻すら漏らしたくない)か?
- YES → UUID v4 か NanoID
- NO → 戻って 2 を再検討
長さの制約がきつい(URL 短縮・QR・SMS)か?
- YES → NanoID(21 文字 or それ以下)/ULID(26 文字)
- NO → UUID 系
用途ごとの推奨(早見表)
- DB 主キー(新規) → UUID v7
- DB 主キー(数値必須) → Snowflake
- 外部公開する短縮 URL → NanoID(12〜16 文字)
- 匿名性が要る共有リンク → UUID v4 か NanoID
- CLI で人がコピペする ID → ULID(紛らわしい文字を回避済み)
- ログ・トレース ID → UUID v7 か ULID
まとめ
UUID v4 一択の時代は終わりつつあります。書き込みが多い主キーは v7 か ULID、外部公開する短いトークンは NanoID、数値カラムが要れば Snowflake、というのが 2026 年現在の標準的な分業です。
各形式の生成は ULID / NanoID / Snowflake 生成ツール で試せます。UUID v4 / v7 は UUID 生成ツール を使ってください。