UUID v4・v7・ULIDの違いと使い分け
2026.10.03 更新 · 5分で読めます
UUID v4はすべて乱数、UUID v7とULIDは先頭が時刻なので生成順に並びます。それぞれの構造、データベースのインデックスへの影響、生成時刻が見えてしまう点を比べて、主キーにどれを使うかを整理します。
UUID生成をすぐに使う新しいテーブルの主キーなら、まずUUID v7を検討してください。RFC 9562で定義された標準で、生成時刻の順に並ぶため、新しい行がインデックスの片側にまとまります。作成時刻を知られたくないIDにはUUID v4を、同じ考え方で26文字の短い文字列がほしいときはULIDを使います。3種類ともUUID生成で作成でき、手元の値の解析もできます。
ひと目でわかる比較
| 項目 | UUID v4 | UUID v7 | ULID |
|---|---|---|---|
| 定義 | RFC 9562 5.4節 | RFC 9562 5.7節 | ULID仕様 |
| サイズ | 128ビット | 128ビット | 128ビット |
| 中身 | 乱数122ビット、バージョン、バリアント | UNIXミリ秒48ビット、残り74ビット、バージョン、バリアント | UNIXミリ秒48ビット、乱数80ビット |
| 文字列 | 36文字(16進数とハイフン) | 36文字(16進数とハイフン) | 26文字(Crockford Base32) |
| 生成順に並ぶか | 並ばない | 並ぶ | 並ぶ |
| 生成時刻 | 含まない | ミリ秒までわかる | ミリ秒までわかる |
v7の74ビットは基本的に乱数です。RFC 9562は、その一部をカウンターやミリ秒未満の時刻に使うことも認めています。
時刻が入る位置
UUIDは16進数32桁を8-4-4-4-12に区切って書きます。3つ目のグループの先頭がバージョン、4つ目のグループの先頭にはバリアントが入り、RFC 9562のUUIDでは8・9・a・bのいずれかです。
v7は先頭の16進数12桁がUNIXミリ秒です。2026-01-01T00:00:00.000Z(日本時間の午前9時)、つまり1767225600000 msを入れると次のようになります。
UNIX時間(ms) 1767225600000 = 0x019b76daa800
UUID v7 019b76da-a800-7… 時刻のあとにバージョン7
ULID 01KDVDNA00… 同じ時刻をBase32の10文字で
UUID v4 5962b53c-5d43-4d66-944c-1a3d4c7b6e7c すべて乱数、時刻なし
v4の行はcrypto.randomUUID()で作った値の1つで、作るたびに変わります。逆に019b76da-a800-7000-8000-000000000000をツールの解析タブに貼り付けると、バージョン7、バリアントRFC 9562、時刻2026-01-01T00:00:00.000Zと表示されます。
ULIDは先頭10文字が時刻48ビット、残り16文字が乱数80ビットです。文字はCrockford Base32(0123456789ABCDEFGHJKMNPQRSTVWXYZ)で、紛らわしいI・L・O・Uを除き、大文字と小文字を区別しません。上位の桁が先に来るので、文字列として並べ替えると時刻順になります。
データベースのインデックスで差が出る理由
RFC 9562は、時刻順のバージョンを追加した理由としてインデックスの局所性を挙げています。v4のように時刻順でないUUIDは、続けて作った値がインデックス上で近くに並ばず、挿入がランダムな位置で起こるため、Bツリー系の構造では性能への影響が大きくなりうる、という説明です。v7やULIDは新しいキーがほぼ常にそれまでのキーより大きいので、挿入がインデックスの端に集まります。
RFCの6.11節は、この差が実環境で1桁(10倍)以上になりうると述べています。これはRFCの記述であり、本記事で計測した値ではありません。実際の効果はテーブルの大きさ、ストレージエンジン、インデックスがメモリにどれだけ載るかで変わるため、稼働中のテーブルを変更する前に計測してください。
- 文字列ではなく128ビットの値で保存します。 RFCの6.13節は、36文字の文字列は288ビットを使うため、可能なら128ビットのバイナリ値で保存するよう勧めています。データベースにUUID型があればそれを使います。
- できればデータベースに生成させます。 RFCは、単一のデータベースを使うアプリケーションではデータベースが生成したUUIDのほうが順序を保ちやすいと述べています。2026年10月時点で、PostgreSQL 18のドキュメントには
uuidv7()関数がuuidv4()と並んで載っています。
同じミリ秒の中での順序
時刻はミリ秒単位なので、同じミリ秒に作ったIDの順序は時刻のあとのビットで決まります。
- UUID v7: RFC 9562の6.2節は、順序を保つ方法として固定長カウンター、単調増加する乱数、より細かい時刻の3つを紹介しています。どれを使うか、使わないかは生成器によって異なります。
- ULID: 仕様は、同じミリ秒の中での並び順は保証されないとし、単調生成を使う場合は乱数部分を1ずつ増やすと説明しています。
別々のサーバーで作ったIDは、それぞれのサーバーの時計にも左右されます。時刻ベースのIDの順序は「おおよそ生成順」と考え、正確な順序が必要ならシーケンスや時刻の列を別に持ってください。RFCもUUIDをなるべく解析せず不透明な値として扱うよう勧めているため、作成時刻はキーから取り出さずcreated_atのような列に保存するのがよいでしょう。
v4を使い続けるべき場面
v7とULIDは、IDを見た人に作成時刻を伝えます。公開URLに入るIDなら、登録日時やアカウント同士の前後関係がわかってしまうことがあります。RFC 9562はこれをごく小さな攻撃対象領域と表現し、セキュリティに関わる用途でUUIDが必要ならv4を使うよう勧めています。
ただし、どのUUIDも秘密の値ではありません。RFCは、UUIDが推測しにくいと仮定してはならず、持っているだけでアクセスが許可される値として使ってはならないと明記しています。セッショントークン、パスワード再設定リンク、APIキーには専用のランダムなトークンを使い、サーバー側で権限を確認してください。
| 場面 | 選び方 |
|---|---|
| 新しいテーブルの主キー、イベント・ログのID | UUID v7 |
| 公開されるIDで作成時刻を知られたくない | UUID v4 |
| 短くてURLに入れやすい時刻順の文字列 | ULID |
| 問題なく使えている既存のv4キー | そのまま(どちらも同じUUID) |
ZEKILO Devでの手順
- UUID生成でUUID v4・UUID v7・ULIDのいずれかを選びます。
- 個数(1個から1,000個まで)と、大文字、ハイフン、波かっこや引用符で囲むかどうかを決めます。
- 手元の値は解析タブに貼り付けて、バージョンとバリアント、含まれている時刻(v1・v6・v7・ULID)を確認します。
値はブラウザの暗号APIを使ってこの端末で生成し、どこにも送信しません。
まとめ
- UUID v4は乱数122ビットです。順序も時刻も含みません。
- UUID v7とULIDは先頭48ビットがミリ秒の時刻で、生成順に並び、新しいインデックス項目が1か所にまとまります。
- 時刻順のIDは作成時刻を伝えます。それが問題になる場面ではv4を使い、どのUUIDも秘密の値として扱わないでください。
- UUIDは128ビットの値で保存し、正確な順序が必要なら時刻の列を別に持ちます。