UUID v4 / v7 / NanoID / CUID2 使い分け完全ガイド
識別子の選択がシステム設計に与える影響
分散システムやデータベースで使われる識別子(ID)は、パフォーマンス・ソート順・セキュリティに直結します。単純に「UUIDを使えばいい」という判断が、後からインデックスの断片化やパフォーマンス問題につながることは珍しくありません。
UUID v4、UUID v7、NanoID、CUID2——それぞれに異なる設計思想があります。この記事では各仕様を参照しながら特性を比較し、ユースケースに応じた選択基準を整理します。
UUID の仕様概要(RFC 9562)
UUID(Universally Unique Identifier)の現行仕様は RFC 9562(2024年5月公開)です。以前の RFC 4122 を廃止して改訂されており、UUID v6 / v7 / v8 が新たに定義されました。
UUID は 128 ビット(16バイト)の値で、ハイフン区切りの 16進数テキスト表現(xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx)が広く使われています。4ビットのバージョン番号(M)と、バリアントを示すビット(N)がフォーマットの一部に含まれます。
現在よく使われるバージョンは以下の通りです。
| バージョン | 生成方式 | 時系列ソート |
|---|---|---|
| v1 | タイムスタンプ + MACアドレス | 可(不完全) |
| v4 | 完全ランダム | 不可 |
| v7 | Unix Epoch + ランダム | 可 |
UUID v4 ── ランダム識別子の標準
構造
UUID v4 の 128 ビットのうち 122 ビットがランダムです。残りの 6 ビットはバージョン(0100)とバリアント(10)に使われます。
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
^ ^
version=4 variant bits
衝突確率
122 ビットのランダム空間は 2^122(約 5.3 × 10^36)通りです。誕生日問題の公式により、50%の確率で衝突が発生するのは約 2.71 × 10^18 個生成した時点です。実用上、意図的な攻撃がない限り衝突はほぼ起きません。
// Node.js 標準の crypto モジュール
const { randomUUID } = require('crypto');
const id = randomUUID();
// => "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// ブラウザ Web Crypto API
const id = crypto.randomUUID();
主な特性
- 予測不可能性: 暗号学的に安全な乱数生成器(CSPRNG)を使うため、連続するIDから次のIDを推測できない
- 時系列ソート不可: IDをそのままデータベースのクラスター化インデックスに使うと、ランダムな挿入位置になりB-treeの断片化が起きる
- 広い普及: ほぼすべての言語・フレームワークでネイティブサポート
向いているユースケース
- セキュリティが重要でIDの生成順を隠したい場合(URL中に露出するリソースIDなど)
- 小〜中規模のデータセットで挿入パフォーマンスが許容できる場合
- 単純さを優先したい場合
UUID v7 ── 時系列ランダム識別子
RFC 9562 で新たに定義された UUID v7 は、時系列ソート可能でありながら適切なランダム性を持つ識別子です。
構造
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unix_ts_ms | ver | rand_a |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|var| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rand_b |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
上位 48 ビットが Unix ミリ秒タイムスタンプ(unix_ts_ms)です。残りはバージョン・バリアント・ランダムビット(rand_a / rand_b)で構成され、ランダム部分は 74 ビットあります。
// uuidv7 パッケージ(npm)
import { uuidv7 } from 'uuidv7';
const id1 = uuidv7(); // "0191c0f2-87b2-7000-b3c5-8a1e3b2d5678"
const id2 = uuidv7(); // "0191c0f2-87b4-7001-a2f3-7c9d0e1f2a3b"
// id1 < id2 が文字列比較で成立する(時系列順)
UUIDv7 がデータベースにやさしい理由
多くのデータベース(MySQL InnoDB、SQL Server)では、主キーにB-treeクラスター化インデックスが使われています。UUID v4 のようなランダム値を挿入すると、ページの中間への挿入が頻発してページ分割・断片化が起きます。
UUID v7 はタイムスタンプ先頭のため、ほぼ末尾への追記になり断片化が大幅に減ります。PostgreSQL でのベンチマークでは、大規模データセットで UUID v4 比 30〜50% の挿入速度向上が報告されています。
-- PostgreSQL 17 以降(uuid_generate_v7 は拡張不要)
-- uuid-ossp 拡張または pgcrypto が一般的
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- v4
-- id UUID PRIMARY KEY DEFAULT uuid_generate_v7(), -- v7(拡張必要)
created_at TIMESTAMPTZ DEFAULT NOW()
);
向いているユースケース
- 挿入が多い大規模テーブルの主キー
- 時系列でのソート・ページネーションが必要な場合
- UUID の標準フォーマット(128 ビット)を維持したい場合
NanoID ── コンパクトでURL安全なID
NanoID(Andrey Sitnik 作)は 2017 年に公開された軽量な識別子ライブラリです。UUID の代替として設計されており、デフォルトで 21 文字の URL 安全な文字列を生成します。
設計思想
NanoID はアルファベット(デフォルト: A-Za-z0-9_- の 64 文字)と長さを自由に指定できます。UUID より短い表現で同等以上のランダム性を実現します。
デフォルト (21 文字, 64文字アルファベット):
ランダムビット数 = 21 × log2(64) = 21 × 6 = 126 ビット
UUID v4 の 122 ビットより若干多く、実用上の衝突確率は同等以下です。
// npm: nanoid
import { nanoid, customAlphabet } from 'nanoid';
const id = nanoid(); // "V1StGXR8_Z5jdHi6B-myT"
const shortId = nanoid(10); // "IRFa-VaY2b"
// カスタムアルファベット(数字のみ)
const numericId = customAlphabet('0123456789', 12);
console.log(numericId()); // "483920571038"
// 大文字英数字のみ(human-readable)
const humanId = customAlphabet('ABCDEFGHJKLMNPQRSTUVWXYZ23456789', 8);
console.log(humanId()); // "A7XK3M2P"
UUID との比較
| 項目 | UUID v4 | NanoID (default) |
|---|---|---|
| 文字数 | 36(ハイフン含む) | 21 |
| ランダムビット | 122 | 126 |
| URL 安全 | いいえ(ハイフンは安全だが長い) | はい |
| ソート可能 | いいえ | いいえ |
| バイト数(UTF-8) | 36 bytes | 21 bytes |
| 標準化 | RFC 9562 | なし |
向いているユースケース
- URLに直接使われるID(短縮URLのスラッグなど)
- フロントエンドやモバイルアプリで生成するID(軽量性が重要)
- UUID の長さが UI に不向きな場面
CUID2 ── 衝突耐性と分散対応
CUID2 は Eric Elliot が提唱した CUID の後継で、2022 年に設計を刷新しました。元の CUID はタイムスタンプとカウンタの組み合わせでしたが、CUID2 は SHA-3 ベースのハッシュに変更されています。
設計目標
CUID2 の主な設計目標は以下の通りです:
- 分散環境での衝突耐性: 複数サーバー・複数プロセスで同時に生成しても衝突しない
- 時系列の情報を含まない: タイムスタンプを先頭に持たないため、生成時刻を推測されない
- Fingerprint の組み込み: 環境情報(ホスト名ハッシュなど)をエントロピーに追加
// @paralleldrive/cuid2
import { createId } from '@paralleldrive/cuid2';
const id = createId(); // "clh3x5y9b0000qzrmn3b24bmi"
// 常に小文字英数字。先頭は常に英字
内部構造
[先頭文字(英字)] + [SHA-3ハッシュ(タイムスタンプ + フィンガープリント + ランダム)]
デフォルト長は 24 文字です。カスタマイズも可能:
import { init } from '@paralleldrive/cuid2';
const createCustomId = init({
length: 32, // 長さを変更
fingerprint: 'my-app-server-1', // サーバー識別子
});
console.log(createCustomId()); // "c..." (32文字)
向いているユースケース
- 多数のサーバーが同時にIDを生成するマイクロサービス環境
- ID の生成時刻を外部に公開したくない場合
- 小文字英数字のみの制約がある場合(一部のシステムでは大文字を扱えない)
4つの識別子を総合比較
| 特性 | UUID v4 | UUID v7 | NanoID | CUID2 |
|---|---|---|---|---|
| ランダムビット数 | 122 | 74 | 126 | ~160+ |
| 時系列ソート | ✗ | ✓ | ✗ | ✗ |
| URL 安全 | △ | △ | ✓ | ✓ |
| 標準規格 | RFC 9562 | RFC 9562 | なし | なし |
| 文字数(デフォルト) | 36 | 36 | 21 | 24 |
| 衝突確率(10億生成時) | 無視できる | 無視できる | 無視できる | 無視できる |
| DB 挿入効率(大規模) | 低い | 高い | 低い | 低い |
| 予測不可能性 | 高い | 高い | 高い | 高い |
| 依存ライブラリ | 不要 | 必要 | 必要 | 必要 |
データベース主キー選択の判断基準
状況別推奨
PostgreSQL を使う場合: PostgreSQL は UUID を native 型としてサポートし、UUID v4 でも十分なパフォーマンスを発揮します。ただし挿入が毎秒数千件を超えるテーブルでは UUID v7 の検討価値があります。
-- PostgreSQL での UUID v7(uuid-ossp v2 または pgcrypto 拡張)
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- uuid_generate_v7() は拡張バージョンにより利用可否が異なる
-- 代替: アプリ層で生成して INSERT
MySQL / MariaDB を使う場合: InnoDB のクラスター化インデックス構造上、UUID v4 はランダム挿入の問題が顕著です。UUID v7 か、バイナリ(16)型への格納が有効です。
-- MySQL で UUID をバイナリとして格納(パフォーマンス改善)
CREATE TABLE users (
id BINARY(16) PRIMARY KEY DEFAULT (UUID_TO_BIN(UUID(), 1)),
-- 第2引数 1 は swap_flag: タイムスタンプバイトを先頭に移動(UUID v1のみ有効)
email VARCHAR(255) NOT NULL
);
MongoDB を使う場合: MongoDB は ObjectID(12バイト、タイムスタンプ先頭)をデフォルト ID として使います。UUID に移行する場合は UUID v7 が ObjectID に近い特性を持ちます。
セキュリティ上の注意
UUID v7 の先頭 48 ビットはタイムスタンプです。URLに直接露出する場合、リソースの作成時刻が漏洩します。これが問題になる場合は UUID v4 か NanoID を選択してください。
https://example.com/api/items/0191c0f2-87b2-7000-b3c5-8a1e3b2d5678
^^^^^^^^^^^^^^^^
タイムスタンプ(2024年頃と推測可能)
実装例:Next.js でのID生成
// src/lib/id.ts
// サーバーサイド: crypto.randomUUID() (Node.js 組み込み)
// クライアント対応: uuidv7 パッケージ
import { uuidv7 } from 'uuidv7';
import { nanoid } from 'nanoid';
// データベース主キー用(時系列ソート可能)
export function generateRecordId(): string {
return uuidv7();
}
// URL スラッグ用(短くURL安全)
export function generateSlug(length = 10): string {
return nanoid(length);
}
// APIキー用(長くランダム)
export function generateApiKey(): string {
// 32文字の英数字 + 記号で256ビット相当
const { customAlphabet } = require('nanoid');
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return customAlphabet(alphabet, 32)();
}
UUID 生成ツール
UUID の生成や形式の確認には UUID ジェネレーター を使えます。v4 と v7 の両方に対応しており、バルク生成もできます。
まとめ
| 選択基準 | 推奨 |
|---|---|
| 大規模テーブルの主キー | UUID v7 |
| セキュリティ重視のリソースID | UUID v4 |
| URLに使う短いID | NanoID |
| マイクロサービスの分散ID生成 | CUID2 |
| 標準規格への準拠が必要 | UUID v4 または v7(RFC 9562) |
識別子の選択に「唯一の正解」はなく、システムの規模・セキュリティ要件・データベースエンジンの組み合わせで決まります。UUID v7 はデータベースパフォーマンスと標準性を両立する選択肢として、新規プロジェクトでの採用が増えています。
参考文献
- RFC 9562 — Universally Unique IDentifiers (UUIDs) (2024年5月、IETF)
- NanoID GitHub リポジトリ — Andrey Sitnik
- CUID2 GitHub リポジトリ — ParallelDrive
- uuidv7 npm パッケージ — LiosK
