はじめに
プライマリキーを連番(1, 2, 3...)からUUIDに変更したら、データベースの書き込み性能が大幅に劣化した経験はありませんか?
この記事では、UUIDv4とUUIDv7の違いと、なぜv7を使うべきなのかを解説します。
UUIDの普及状況
現状
UUIDv4が圧倒的に普及しています
理由
- 最もシンプル(ランダム生成するだけ)
- ライブラリのデフォルトになっていることが多い
- 歴史的に長く使われてきた
UUIDv7について
2024年5月にRFC 9562で標準化されたばかりで、これから普及していく段階です。
フォーマットの違い
UUIDv4(ランダム)
a1b2c3d4-e5f6-4789-a012-b3c4d5e6f789
^^^^ ^
| |
バージョン4 バリアント
特徴
- 完全にランダム
- 順序性なし
- 例:
f47ac10b-58cc-4372-a567-0e02b2c3d479
UUIDv7(タイムスタンプベース)
018c7e1a-2b3c-7def-8901-234567890abc
^^^^^^^^^^^^^ ^
| |
タイムスタンプ バージョン7
(ミリ秒精度)
特徴
- 先頭48ビット: Unixタイムスタンプ(ミリ秒)
- 残り: ランダム
- 例:
018c7e1a-2b3c-7def-8901-234567890abc
実際の比較
# 同じ時間に生成したUUID
【UUIDv4】
550e8400-e29b-41d4-a716-446655440000
9b6d5e2a-1c3f-4d8e-9a7b-3c2d1e0f9a8b ← バラバラ
c8f7e6d5-4b3a-4291-8807-f6e5d4c3b2a1
【UUIDv7】
018c7e1a-2b3c-7001-8901-234567890abc
018c7e1a-2b3c-7002-8902-345678901bcd ← 順序性がある
018c7e1a-2b3c-7003-8903-456789012cde
なぜUUIDv4は性能が悪いのか
データベースのインデックスは多くの場合B-Tree(バランス木)構造を使用しています。
連番IDの場合(1, 2, 3...)
✅ 新しいデータは常にインデックスの最右端に追加される
✅ 書き込みは予測可能で順次的
✅ キャッシュヒット率が最大化
✅ ページは100%満杯に保たれる
UUIDv4の場合
❌ ランダムな値なので、挿入位置がツリー内のどこにでも発生
❌ ランダムI/Oが発生(ディスクから常にランダムなページをメモリにロード)
❌ ページ分割が頻発(ページが満杯になると半分に分割)
❌ スイスチーズ効果(インデックスが穴だらけになり、RAMとディスク容量を浪費)
なぜデータベースサイズが重要なのか
データベースは頻繁にアクセスするデータをRAM(メモリ)にキャッシュしています。
【データベース ≦ RAM の場合】
✅ すべてのインデックスページがメモリ内
✅ UUIDv4でもメモリ内で完結
✅ 性能劣化は比較的小さい
【データベース > RAM の場合】
❌ インデックスの一部しかメモリにない
❌ UUIDv4のランダムアクセスでメモリにないページへアクセス
❌ ディスクI/Oが頻発(メモリの約100,000倍遅い)
❌ 書き込み性能が20-90%劣化
連番IDやUUIDv7の場合
データベースサイズがRAMを超えても、新しいデータは常に最新ページ(最右端)に追加されるため、最新ページだけメモリに保持すればよく、ディスクI/Oは最小限で済みます。
結果
インデックスサイズがRAMを超えると、UUIDv4は書き込み性能が20-90%劣化する可能性
UUIDv7の利点
UUIDv7はタイムスタンプを先頭に埋め込むことで、この問題を解決します。
1. 書き込み性能が大幅改善
- タイムスタンプベースで単調増加するため、B-Treeインデックスの最右端に追加される
- ページ分割が激減し、インデックスの断片化を防げる
- UUIDv4と比較して20-90%の性能劣化を回避できる
2. 分散生成が可能
- 中央集権的なカウンターが不要
- 複数のサーバーやアプリケーションインスタンスで独立して生成できる
3. 列挙攻撃への耐性
- 連番(1, 2, 3...)と違い、次のIDを推測されにくい
UUIDv7のデメリット
タイムスタンプの漏洩
❌ UUIDv7はレコード作成時刻を含むため、その情報が推測可能
❌ セキュリティ要件によっては問題になる可能性
ただし
タイムスタンプ漏洩が致命的なセキュリティリスクになるケースは稀です。
トレードオフの整理
| 項目 | UUIDv4 | UUIDv7 |
|---|---|---|
| 書き込み性能 | ❌ 劣化(20-90%) | ✅ 良好(連番並み) |
| 分散生成 | ✅ 可能 | ✅ 可能 |
| 列挙攻撃耐性 | ✅ 強い | ✅ 強い |
| タイムスタンプ秘匿 | ✅ 秘匿 | ❌ 漏洩 |
| インデックス断片化 | ❌ 発生 | ✅ 発生しない |
失うもの
- タイムスタンプの秘匿性(いつ作成されたかがバレる)
得るもの
- 書き込み性能の大幅改善(20-90%の劣化を回避)
- インデックスの断片化防止
- ディスクI/Oの削減
- RAMの効率的な使用
結論
基本方針
タイムスタンプ漏洩のリスクよりも、性能面のメリットの方が圧倒的に大きい
UUIDv7への移行を強く推奨するケース
-
書き込み性能が重要なアプリケーション
- 高頻度の書き込みが発生する
- レスポンスタイムが重要
-
データベースが成長していく見込みがある
- 将来的にRAMを超えるサイズになる可能性
- スケーラビリティを事前に確保したい
-
分散システムでIDを生成する必要がある
- 複数サーバーで独立してID生成
- 中央カウンターを避けたい
-
すでにUUIDv4で性能問題が発生している
- 書き込み性能が劣化している
- インデックスの肥大化が問題になっている
移行戦略
新規プロジェクト
最初からUUIDv7を採用することで、将来の性能問題を予防できます。
既存システム
新規テーブルや重要なテーブルから段階的にUUIDv7へ移行する価値があります。