PostgreSQLでUUIDを主キーとして使うことについて
(maciejwalkowiak.com)- UUIDはデータベーステーブルの主キーとしてよく使われる
- 生成が簡単で、分散システム間で共有しやすく、一意性を保証できる
- UUIDのサイズを考えるとこれが正しい選択なのか疑問に思うが、多くの場合こちらで決められない
- この記事は「UUIDがキーに適した形式か」に焦点を当てるのではなく、PostgreSQLでUUIDを主キーとして効率的に使う方法を説明する
PostgreSQLでUUIDを主キーとして使う
- UUIDとは?
- UUIDはデータベーステーブルの主キーとしてよく使われる
- 分散システム間で簡単に共有でき、一意性を保証する
- UUIDのサイズのため適切か疑問に思うこともあるが、選択の余地がないことも多い
PostgreSQLのUUIDデータ型
-
UUIDを文字列として保存
- PostgreSQLは文字列を保存するための
textデータ型を提供している - しかし
text型はUUIDの保存には適していない - PostgreSQLはUUID専用のデータ型
uuidを提供している uuid型は128ビットのデータ型で、1つの値を保存するのに16バイト必要text型には1バイトまたは4バイトのオーバーヘッドが追加される
- PostgreSQLは文字列を保存するための
-
実験結果
- 2つのテーブルを作成して比較: 片方は
text型、もう片方はuuid型 - 10,000,000行を挿入した後、テーブルサイズとインデックスサイズを比較
text型を使うテーブルは54%大きく、インデックスサイズは85%大きい
- 2つのテーブルを作成して比較: 片方は
UUIDとB-Treeインデックス
-
B-TreeインデックスとUUID
- ランダムUUIDはB-Treeインデックスに向いていない
- B-Treeインデックスは順序のある値とうまく機能する
- Javaの
UUID.randomUUID()はUUID v4を返し、これは疑似ランダム値である - UUID v7は時系列順に並ぶ値を生成するため、B-Treeインデックスに適している
-
UUID v7の使用
- JavaでUUID v7を使うには
java-uuid-generatorライブラリが必要 - UUID v7を生成すると挿入性能が向上する可能性がある
- JavaでUUID v7を使うには
UUID v7がINSERT性能に与える影響
- 実験
- UUID v7を使うテーブルを作成し、10,000行を10回挿入して性能を測定
- 結果はややランダムだが、UUID v7の挿入はおよそ2倍高速
追加の読み物
- PostgreSQL 17でUUID v7がネイティブサポートされる可能性がある
- UUID v7形式に関する情報
- UUIDがデータベース主キーとしての性能に与える影響
まとめ
-
UUIDの長さの問題
- 最適化しても、UUIDは主キーとして最適な型ではない
- 選択の余地があるなら、TSIDのような別の選択肢を検討すること
-
最適化の必要性
- 大規模データセットや高トラフィックが見込まれるなら、最適化を検討すべき
- 主キーの変更は難しい作業なので、最初から正しく設定することが重要
-
注意事項
- 筆者はPostgreSQLの専門家ではなく、学んだ内容を共有しているだけ
- 役に立ったなら、コメントやTwitterでフィードバックを送ってほしい
GN⁺のまとめ
- この記事は、PostgreSQLでUUIDを主キーとして使う際の効率的な方法を扱っている
- 実験を通じて、UUID v7を使うと挿入性能が向上する可能性を示している
- 大規模データセットや高トラフィックが予想される場合は最適化が必要
- TSIDのような別の選択肢も検討する価値がある
4件のコメント
UUID に標準形式(16進数 + ハイフン)ではなく base62 エンコーディングを望むのは無理でしょうか?
uuidv7 は無敵だ
uuidv8+ は「神」だ
一番大きなハードルは、、人に優しくないこと…。自分はまだ多くの場面でこの点が必要ですね…。
Hacker Newsの意見
B-tree に適した主キーとして
bigserialを使い、外部レコードロケータの選択肢として文字列エンコードされた UUID を検討することを推奨hashidsは使わないこと。暗号学的な品質がなく、一般の人にもなじみが薄いデータベーススキーマ設計では、関心の分離と機械的共感の原則を念頭に置くこと
Stripe の型付きランダム ID は、実際にはランダムではない
bigserial+HMAC ロケータを好むPostgres ではランダム UUID は大きな問題ではない
serial(4バイト)やbigserial(8バイト)より大きいが、テーブル全体のレベルでは大きな問題ではないPostgres で
serialvs. random UUID vs. ordered UUID を考える前に、心配すべきことは他にもたくさんある最近 Postgres の PK として ULID を選び、この記事がとても参考になった: https://brandur.org/nanoglyphs/026-ids
ULID を好む理由は、UUID 型と互換性があり、タイムスタンプが組み込まれているため、ID でソートするとタイムスタンプ順に並ぶから
比較に
int64も含まれていれば、UUID と従来のアプローチのオーバーヘッドを比較できてよいはず挿入性能は性能評価の方法としては適切ではない
SQLite で UUID4 が好まれる理由は、トランザクションロック中にページキャッシュの競合が起きる可能性が低いため
整数の自動増分主キーを好む
UUIDv7 の挿入時間ベンチマークには UUID 生成時間が含まれている
PostgreSQL 17 に UUIDv7 サポートが含まれる可能性は低い
python-ulidを使い始めたが、ULID は UUID より優れているUUID v7 の標準リンクは古くなっているので、RFC 9562 を参照すること: https://datatracker.ietf.org/doc/html/rfc9562