データベース主キーとしての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');

インデックス戦略:

  1. UUID v7でのクラスターインデックス: INSERTがほぼシーケンシャルであるため安全で高パフォーマンスです。
  2. UUID v4でのクラスターインデックス: 避けてください。ランダムINSERTがページ分割とフラグメンテーションを引き起こします。
  3. セカンダリUUIDインデックス + オートインクリメントのクラスターインデックス: クラスターインデックスが物理的な行の順序を決定するMySQL/InnoDBにおいて、両方の利点を得られます。

JOINパフォーマンス: UUID JOINは整数JOINよりも遅くなります。16バイトの比較は4または8バイトの比較よりも遅いためです。JOINが多いテーブルでは、ハイブリッドアプローチ(内部JOINには整数PK、外部参照にはUUID)を検討してください。

ユースケース

UUIDを主キーとして使用することは、各リージョンが独立して書き込みを行い、レプリケーション時にデータを衝突なくマージする必要があるマルチリージョンデータベースデプロイメントにおいて不可欠です。

試してみる — UUID Generator

フルツールを開く