6月5日、Anders Murphyが「The perils of UUID primary keys in SQLite」と題した記事を公開した。
衝撃の実験結果:UUID主キーは12倍遅い
SQLiteで1億行のデータ挿入テストを行った結果、UUID4主キーは整数主キーと比較して12倍以上遅いという衝撃的な結果が明らかになった。この性能差は、多くの開発者が見落としがちな、データベースの根本的な仕組みに起因している。
実験データを見てみよう。整数主キーでは1億行の挿入に1,081ミリ秒しかかからなかったのに対し、UUID4主キーでは12,586ミリ秒を要した。つまり、毎秒100万行から毎秒8万行へと性能が激減している。
なぜUUID主キーが人気なのか
まず、なぜUUID主キーが広く使われるのかを理解する必要がある。従来の自動増分整数主キーには以下の問題がある:
- 分散システムでの衝突リスク:複数のデータベースインスタンス間でIDが重複する
- 情報漏洩のリスク:連番からレコード数や作成順序が推測される
- 外部露出時のセキュリティ懸念:APIのURLに含めるとシステム規模が露呈
UUID(Universally Unique Identifier)は128ビットの識別子で、理論上重複の可能性が極めて低い(UUID4の場合、約5.3×10^36通り)。そのため、マイクロサービス、API設計、データ移行において重宝されている。
問題の根本:クラスタードインデックスとB-tree構造
SQLiteはrowidという64ビット整数でデータを物理的に順序付けて格納する。この仕組みをクラスタードインデックスと呼ぶ。データは主キーの値順にB-tree構造で配置され、範囲検索や順次アクセスが高速化される。
しかし、UUID4のようなランダムな値を主キーにすると:
- 挿入のたびにB-treeの再バランスが発生
- ページの分割・結合が頻繁に起こる
- ディスクI/Oが大幅に増加
- キャッシュ効率が悪化
著者のプロファイリングでは、UUID4使用時にSQLiteがB-treeの再バランス、読み取り、書き込みに大幅に多くの時間を費やしていることが確認された。
詳細な実験結果
整数主キー(ベースライン)
CREATE TABLE event(id INT PRIMARY KEY, data BLOB)
| 総行数 | 時間(ms) | 1秒あたりの挿入行数 |
|---|---|---|
| 10,000,000 | 1,208 | 約830万行 |
| 50,000,000 | 1,086 | 約4,600万行 |
| 100,000,000 | 1,081 | 約9,250万行 |
UUID4主キー
CREATE TABLE event(id BLOB PRIMARY KEY, data BLOB) WITHOUT ROWID
| 総行数 | 時間(ms) | 1秒あたりの挿入行数 | 性能比 |
|---|---|---|---|
| 10,000,000 | 2,649 | 約380万行 | 2.2倍遅い |
| 50,000,000 | 9,359 | 約530万行 | 8.6倍遅い |
| 100,000,000 | 12,586 | 約795万行 | 11.6倍遅い |
注目すべきはWITHOUT ROWIDオプションの使用だ。これにより、テーブルの物理的順序がUUID値で決定され、ランダムな挿入パターンが性能劣化を引き起こす。
解決策:時系列順UUID7の登場
この問題を解決するのがUUID7だ。UUID Version 7は2022年に提案された新しい仕様で、以下の特徴を持つ:
- 最初の48ビット:Unixタイムスタンプ(ミリ秒精度)
- 次の12ビット:同一ミリ秒内での順序保証用カウンタ
- 残り74ビット:ランダム値
この設計により、時間順でソート可能でありながら、UUID1のようなMACアドレス露出リスクを回避している。
UUID7主キーの結果
| 総行数 | 時間(ms) | 整数主キーとの差 |
|---|---|---|
| 10,000,000 | 1,372 | 1.3倍(許容範囲) |
| 50,000,000 | 1,256 | 1.2倍(許容範囲) |
| 100,000,000 | 1,258 | 1.2倍(許容範囲) |
わずかな性能差(UUID7は16バイト、整数は8バイト)はあるが、実用的なレベルを維持している。
他のデータベースでも共通する問題
この問題はSQLite特有ではない。クラスタードインデックスを使用する主要データベースで同様の報告がある:
- MySQL InnoDB:公式ドキュメントで主キーの順序性の重要性に言及
- PostgreSQL:テーブルの物理的順序とインデックス効率の関係
- SQL Server:クラスタードインデックスの設計ガイドラインでランダムキーの問題を指摘
特にPerconaのMySQL性能ガイドでは、UUID主キーによる性能劣化について詳細な分析が提供されている。
現実的な対策と選択指針
UUID主キーを避けるべきケース
- 大量データの頻繁な挿入がある
- 性能が最優先
- 単一データベース環境
UUID7を検討すべきケース
- 分散システムでの一意性が必要
- 外部APIでIDを露出する
- データ移行やレプリケーションが頻繁
- セキュリティ上、連番IDが適さない
実装時の注意点
- UUID7は比較的新しい仕様のため、ライブラリサポートを確認
- 既存システムからの移行計画を慎重に策定
- アプリケーション層でのUUID生成戦略を統一
まとめ:アーキテクチャ設計の重要性
この実験結果は、「小さな選択が大きな影響を与える」というソフトウェア設計の本質を表している。特に、SQLiteを使用するスタートアップや小規模アプリケーションでは、初期の少量データで問題が表面化せず、後になって深刻なボトルネックとなるリスクがある。
データベース設計において、主キー戦略は後から変更困難な基盤的決定である。性能要件、スケーラビリティ、セキュリティ要求を総合的に評価し、UUID7のような新しい技術選択肢も含めて検討することが重要だ。
詳細はThe perils of UUID primary keys in SQLiteを参照していただきたい。