UUIDをバイナリ(16バイト)で保存する

UUIDを36文字の文字列ではなく16バイトのバイナリで保存すべき理由と方法。55%のストレージ削減、インデックスパフォーマンス向上、メモリ使用量削減を実現します。

Concept

詳細な説明

UUIDは128ビットの値であり、本来16バイトに収まります。しかし多くのアプリケーションでは36文字の文字列表現で格納しており、ストレージの半分以上を無駄にしています。バイナリストレージへの変換により、データベースパフォーマンスを大幅に改善しコストを削減できます。

UUID1つあたりのストレージ比較:

形式 サイズ バイナリとの差分
バイナリ(16バイト) 16バイト 基準値
ハイフンなし16進数 32バイト +100%
標準文字列 36バイト +125%
オーバーヘッド付き(VARCHAR) 37-40バイト +137-150%

大規模環境での影響(1億行、UUIDカラム3つ):

形式 UUID合計ストレージ インデックスサイズ
BINARY(16) 4.47 GB 約3.5 GB
CHAR(36) 10.05 GB 約8.0 GB
削減量 5.58 GB 約4.5 GB

MySQLでのバイナリストレージ:

-- Table definition
CREATE TABLE orders (
    id BINARY(16) PRIMARY KEY,
    customer_id BINARY(16) NOT NULL,
    INDEX idx_customer (customer_id)
);

-- MySQL 8.0+ UUID conversion functions
INSERT INTO orders (id, customer_id)
VALUES (UUID_TO_BIN(UUID(), true), UUID_TO_BIN(@customer_uuid));
-- The 'true' parameter swaps time fields for UUID v1 ordering
-- For UUID v7, use: UUID_TO_BIN(UUID(), false) or just UUID_TO_BIN(UUID())

SELECT BIN_TO_UUID(id, true) as id FROM orders;

アプリケーション層での変換(JavaScript):

// UUID string to 16-byte Buffer
function uuidToBytes(uuid) {
  const hex = uuid.replace(/-/g, '');
  return Buffer.from(hex, 'hex');
}

// 16-byte Buffer to UUID string
function bytesToUuid(bytes) {
  const hex = bytes.toString('hex');
  return [
    hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16),
    hex.slice(16, 20), hex.slice(20)
  ].join('-');
}

アプリケーション層での変換(Python):

import uuid

id = uuid.uuid4()
raw = id.bytes        # 16 bytes
restored = uuid.UUID(bytes=raw)
assert id == restored

サイズ以上にバイナリストレージがパフォーマンスを改善する理由:

  1. インデックスの縮小: B-treeノードがページあたりより多くのキーを保持できるため、ルックアップあたりのディスク読み取りが減少します。16バイトのキーは36バイトのキーと比較して、ページあたり約2倍のエントリを格納できます。
  2. CPU比較速度: 16バイトの比較は36文字の比較よりも高速です。特にCPUが128ビットSIMD命令を使用できる場合に顕著です。
  3. メモリ効率: より小さいインデックスはバッファプール/ページキャッシュに収まりやすく、ディスクI/Oを削減します。
  4. ネットワーク転送: クエリ結果のバイナリUUIDは帯域幅の消費が少なく、大規模な結果セットでは重要です。

デバッグに関する懸念: バイナリUUIDはクエリ結果で人間が読めません。アプリケーション内に変換レイヤーを提供するか、デバッグ時にはデータベース関数(MySQLの BIN_TO_UUID、PostgreSQLの uuid 型では自動変換)を使用してください。

ユースケース

バイナリUUIDストレージは、キーあたり16バイトと36バイトの差がホットインデックスがInnoDBバッファプールに収まるかどうかに直接影響する高トラフィックMySQLアプリケーションにおいて、p99レベルのクエリレイテンシに影響を与えるため非常に重要です。

試してみる — UUID Generator

フルツールを開く