データベース主キーとしてのUUID
UUIDをデータベース主キーとして使う際のベストプラクティス。ストレージ型、インデックス戦略、バージョン選択、パフォーマンス最適化のテクニックを網羅的に解説します。
Usage
詳細な説明
UUIDを主キーとして使用することは現代のアプリケーションでますます一般的になっていますが、適切に行うにはデータベースエンジンごとのストレージ、インデックス、パフォーマンスへの影響を理解する必要があります。
データベース別ストレージオプション:
| データベース | ネイティブ型 | サイズ | 備考 |
|---|---|---|---|
| PostgreSQL | uuid |
16バイト | ネイティブバイナリストレージ、完全サポート |
| MySQL 8+ | BINARY(16) |
16バイト | ネイティブUUID型なし、バイナリを使用 |
| MySQL | CHAR(36) |
36バイト | 一般的だが無駄が多い、可能なら避ける |
| SQLite | BLOB(16) |
16バイト | 可読性にはTEXT(36)も可 |
| MongoDB | BSON内に埋め込み | 16バイト | Binary サブタイプ4を使用 |
黄金ルール:バイナリで保存し、文字列で表示する。 UUIDを CHAR(36) で格納すると、バイナリストレージと比較して1行あたり1つのUUIDカラムにつき20バイトの無駄が生じます。1億行のテーブルにUUIDカラムが3つある場合、約5.6GBの無駄になります。
主キーに適したバージョン選択:
- UUID v7 が新規アプリケーションに最適です。時刻順序の性質によりB-treeに優しいシーケンシャルINSERTを提供しつつ、分散環境での一意性を維持します。
- UUID v4 は使用可能ですがランダムなインデックス分散を引き起こします。約1,000万行以下のテーブルやINSERTスループットが重要でない場合は許容範囲です。
- UUID v1 は時刻順序付きですがMACアドレスを漏洩します。代わりにv7を選択してください。
PostgreSQL向け最適化:
-- Use the native uuid type (stored as 16 bytes)
CREATE TABLE users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
email text NOT NULL UNIQUE,
created_at timestamptz DEFAULT now()
);
-- For UUID v7, use a custom function or extension
CREATE EXTENSION IF NOT EXISTS pgcrypto;
MySQL向けバイナリ最適化:
-- Store as BINARY(16), convert for display
CREATE TABLE users (
id BINARY(16) PRIMARY KEY,
email VARCHAR(255) NOT NULL
);
-- MySQL 8.0+ has UUID_TO_BIN with swap flag for v1 time reordering
INSERT INTO users (id, email) VALUES (UUID_TO_BIN(UUID(), true), 'user@example.com');
インデックス戦略:
- UUID v7でのクラスターインデックス: INSERTがほぼシーケンシャルであるため安全で高パフォーマンスです。
- UUID v4でのクラスターインデックス: 避けてください。ランダムINSERTがページ分割とフラグメンテーションを引き起こします。
- セカンダリUUIDインデックス + オートインクリメントのクラスターインデックス: クラスターインデックスが物理的な行の順序を決定するMySQL/InnoDBにおいて、両方の利点を得られます。
JOINパフォーマンス: UUID JOINは整数JOINよりも遅くなります。16バイトの比較は4または8バイトの比較よりも遅いためです。JOINが多いテーブルでは、ハイブリッドアプローチ(内部JOINには整数PK、外部参照にはUUID)を検討してください。
ユースケース
UUIDを主キーとして使用することは、各リージョンが独立して書き込みを行い、レプリケーション時にデータを衝突なくマージする必要があるマルチリージョンデータベースデプロイメントにおいて不可欠です。