Skip to content
ブログに戻る
チュートリアル

UUID v4 vs v7 vs ULID vs Snowflake:2026年版 分散ID選定ガイド

分散ID選定の実践ガイド。UUID v4・v7・ULID・Snowflake ID・NanoIDをデータベース性能、ソート可能性、ストレージサイズ、エコシステム対応で徹底比較。多言語コード例付き。

15分で読めます

UUID v4 vs v7 vs ULID vs Snowflake:2026年版 データベースに最適なIDの選び方

ID方式の選択を誤ると、大きな代償を払うことになります。1億行のテーブルでランダムなUUID v4をプライマリキーにすると、シーケンシャルIDと比較してインデックスのページスプリットが最大10倍に増加します。Snowflake IDは中央集権的なワーカー登録が必要で、単一障害点になりえます。ULIDは理想的な折衷案に見えましたが、UUID v7がIETF標準として登場しました。

本ガイドでは、意思決定フレームワーク、実測パフォーマンスデータ、コード例を提供し、あなたのシステムに最適な識別子を選ぶお手伝いをします。

クイック選定表

要件最適な選択肢理由
データベースのプライマリキー(新規プロジェクト)UUID v7時系列順、標準の uuid カラム型、インデックス性能が最良
汎用ユニークID(順序不要)UUID v4あらゆる環境で対応、設定不要、122ビットのランダム性
既知の入力から決定論的IDを生成UUID v5同じnamespace + nameで常に同じUUIDを生成
高スループット分散システム(>10万ID/秒/ノード)Snowflake ID64ビット整数、ワーカー内で単調増加、ネイティブ BIGINT 格納
短いURL安全なトークン・クライアント側IDNanoID21文字、URLセーフな文字セット、長さカスタマイズ可能
既にULIDを使用しているレガシーシステムULID現状維持で問題なし。機能的にはUUID v7と同等で、移行のコストに見合わない

UUIDバージョン詳解

UUID v1 ― タイムスタンプ + MACアドレス(非推奨)

UUID v1は60ビットのタイムスタンプとマシンの48ビットMACアドレスをエンコードします。元祖「ソート可能なUUID」でしたが、2つの致命的な欠陥があります。ハードウェアの情報が漏洩すること、そして非標準のタイムスタンプエポック(1582年10月15日)を使用することです。RFC 9562ではv1を正式に非推奨とし、v6/v7を推奨しています。新規プロジェクトではv1を使わないでください。

UUID v4 ― 純粋なランダム

UUID v4は128ビットのうち122ビットを暗号学的に安全な乱数で埋めます。最も広く使われているバージョンで、シンプル、プライバシー保護、あらゆる環境で対応しています。

メリット:

  • 設定不要、調整不要
  • 完全に匿名 ― タイムスタンプもハードウェア情報も漏洩しない
  • すべてのデータベース、言語、フレームワークでネイティブ対応

デメリット:

  • ランダムな分布がB-treeインデックスの断片化を引き起こす。数百万行規模のライトヘビーなテーブルでは、v4プライマリキーの挿入パフォーマンスが過剰なページスプリットにより、シーケンシャルIDと比較して2〜10倍低下する場合があります。
// UUID v4を生成 ― すべてのモダンブラウザとNode.jsに組み込み
const id = crypto.randomUUID();
// → "550e8400-e29b-41d4-a716-446655440000"

UUID v5 ― 決定論的ハッシュ

UUID v5はSHA-1を使ってnamespace UUIDとname文字列をハッシュし、決定論的なUUIDを生成します。同じ入力からは常に同じ出力が得られます。

ユースケース: URL、DNS名、その他の再現可能な入力から安定したIDを生成する場合。v3(より弱いMD5を使用)よりもv5を優先してください。

import uuid

# 同じ入力 → 同じUUID(毎回同一)
id = uuid.uuid5(uuid.NAMESPACE_DNS, "example.com")
# → "cfbff0d1-9375-5685-968c-48ce8b15ae17"

UUID v7 ― 時系列順ランダム(推奨)

UUID v7(RFC 9562、2024年5月)は最上位ビットに48ビットのUnixミリ秒タイムスタンプを埋め込み、続く74ビットに暗号学的乱数を配置します。

なぜv7がデータベースキーの新しいデフォルトなのか:

  • シーケンシャル挿入:新しいUUIDは常に以前のものより大きくなる(ミリ秒精度内)ため、B-treeへの挿入は常にインデックスの末尾に追加される
  • v4と比較して、ライトヘビーなワークロードでページスプリットが最大90%削減
  • 自然な時系列ソートが可能で、追加の created_at カラムが不要
  • 標準の uuid カラム型 ― v4からの移行にスキーマ変更が不要
  • 74ビットのランダム性 ― ほぼすべてのアプリケーションに十分(v4は122ビット)

トレードオフ: 作成タイムスタンプがIDに埋め込まれるため、作成時刻を隠したい不透明なIDが必要な場合はv4を使ってください。

// UUID v7の生成(Node.js 20以降)
import { v7 as uuidv7 } from "uuid";
const id = uuidv7();
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"
// 古いIDは常に新しいIDより前にソートされる

PostgreSQL・MySQLのパフォーマンス比較:v4 vs v7

PostgreSQL 16上の5,000万行テーブル(B-treeプライマリキー)でのベンチマーク結果:

指標UUID v4UUID v7改善幅
挿入スループット(行/秒)12,40028,6002.3倍高速
5,000万行後のインデックスサイズ4.2 GB2.8 GB33%削減
バルクインサート中のページスプリット120万回8.4万回93%削減
インサート後のシーケンシャルスキャン320 ms180 ms44%高速

MySQL/InnoDBでは影響がさらに顕著です。プライマリキーがそのままクラスタードインデックスとなるため、ランダムなv4 UUIDは常にページの再編成を引き起こしますが、v7は自動インクリメントのように振る舞います。

代替ID方式

ULID ― v7登場前のベストチョイス

ULID(Universally Unique Lexicographically Sortable Identifier)は2016年に作られ、UUID v4のソート問題を解決することを目的としていました。48ビットのミリ秒タイムスタンプと80ビットの乱数を、26文字のCrockford Base32文字列にエンコードします。

01AN4Z07BY      79KA1307SR9X4MV3
|----------|    |----------------|
 タイムスタンプ       ランダム
   48 bits          80 bits

ULID vs UUID v7 ― 移行すべきか?

観点ULIDUUID v7
ソート可能はいはい
文字列長26文字36文字
ストレージサイズ16バイト16バイト
標準化コミュニティ仕様IETF RFC 9562
ネイティブDBタイプなし(CHAR(26) または BYTEAあり(uuid
言語サポートnpm、PyPI、crates.io主要な標準ライブラリに組み込み

結論: 新規プロジェクトならUUID v7を選びましょう。同じソート可能性に加え、はるかに優れたエコシステムサポートとネイティブDBタイプが利用できます。既にULIDを使っているシステムでは、急いで移行する必要はありません。両者は機能的に同等です。

Snowflake ID ― 高スループット分散システム向け

Snowflake ID(2010年にTwitterが開発)は以下の情報を64ビット整数にパックします:

0 | 41ビット タイムスタンプ | 10ビット ワーカーID | 12ビット シーケンス番号
  • 41ビットタイムスタンプ:カスタムエポックからのミリ秒数(約69年分の範囲)
  • 10ビットワーカーID:最大1,024のユニークワーカーをサポート
  • 12ビットシーケンス番号:ワーカーごとにミリ秒あたり最大4,096のIDを生成

メリット:

  • 8バイト ― UUID/ULIDの半分、BIGINT カラムに格納可能
  • ワーカー内で単調増加 ― ノードごとの順序を保証
  • 理論上のスループットは1ワーカーあたり毎秒409.6万ID
  • プレーンな整数で人間が読みやすい

デメリット:

  • 中央集権的な調整が必要 ― ワーカーIDを一元的に割り当て・管理する必要がある(通常ZooKeeper、etcd、設定サービスを使用)
  • クロックスキューに敏感 ― システムクロックがずれるとIDの衝突や巻き戻りが発生する可能性がある
  • カスタムエポック ― 各実装が独自のエポックを選ぶため、システム間の相互運用が困難
  • 非標準 ― 数十の互換性のないバリアント(Twitter、Discord、Instagram等)が存在
// Snowflake IDの生成(sony/sonyflakeを使用)
package main

import (
    "fmt"
    "github.com/sony/sonyflake"
)

func main() {
    sf := sonyflake.NewSonyflake(sonyflake.Settings{})
    id, _ := sf.NextID()
    fmt.Println(id) // → 175928847299543040
}

Snowflakeを選ぶべきケース: ノードあたり毎秒10万以上のIDを生成する必要があり、コンパクトな64ビット整数が求められ、ワーカーID割り当てのインフラ(例:Kubernetes Podの序数)が既にある場合。

NanoID ― コンパクトなURLセーフID

NanoIDは文字セット A-Za-z0-9_- を使用して、短い(デフォルト21文字)URLセーフな識別子を生成します。セキュリティには crypto.getRandomValues() を使用しています。

import { nanoid } from "nanoid";
const id = nanoid();    // → "V1StGXR8_Z5jdHi6B-myT"
const short = nanoid(10); // → "IRFa-VaY2b"

適したユースケース: 短縮URL、フロントエンドのコンポーネントキー、招待コード、ファイル名など、文字列の長さが重要で、データベースレベルのソートやシステム間の相互運用が不要な場面。

向かないケース: データベースのプライマリキー(ネイティブDB型がない、ソート不可、タイムスタンプなし)。

CUID2 ― 大規模での衝突耐性

CUID2は水平スケーリング向けに設計された可変長IDを生成します。カウンター、タイムスタンプ、フィンガープリント、ランダム性を組み合わせています。

ニッチなユースケース: 調整なしで多数の独立した生成器間の衝突耐性が必要なシステム。実用上、UUID v7がより優れた標準化でこの要件をカバーしています。

総合比較表

特性UUID v4UUID v7ULIDSnowflakeNanoID
長さ36文字36文字26文字15〜20桁21文字(デフォルト)
ストレージ16バイト16バイト16バイト8バイト約21バイト
ソート可能いいえはい(時系列)はい(時系列)はい(時系列)いいえ
タイムスタンプなし48ビットms48ビットms41ビットmsなし
ランダムビット数122ビット74ビット80ビット12ビットシーケンス約126ビット
標準化RFC 9562RFC 9562コミュニティ仕様プロプライエタリコミュニティ仕様
ネイティブDB型uuiduuidなしBIGINTなし
調整の必要性なしなしなしワーカー登録なし
URLセーフいいえ(ハイフン含む)いいえ(ハイフン含む)はいはい(整数)はい
100万ID生成時の衝突確率~10⁻²²~10⁻¹⁸~10⁻²⁰ゼロ(単調増加)~10⁻²¹

各言語のコード例

JavaScript / TypeScript

import { v4 as uuidv4, v7 as uuidv7 } from "uuid";
import { ulid } from "ulid";
import { nanoid } from "nanoid";

// UUID v4
console.log(uuidv4());
// → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"

// UUID v7
console.log(uuidv7());
// → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"

// ULID
console.log(ulid());
// → "01ARZ3NDEKTSV4RRFFQ69G5FAV"

// NanoID
console.log(nanoid());
// → "V1StGXR8_Z5jdHi6B-myT"

Python

import uuid
from ulid import ULID
from nanoid import generate

# UUID v4
print(uuid.uuid4())
# → "a8098c1a-f86e-11da-bd1a-00112444be1e"

# UUID v7(Python 3.14以降で組み込み予定。現時点ではuuid7パッケージを使用)
from uuid_extensions import uuid7
print(uuid7())
# → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"

# ULID
print(ULID())
# → "01ARZ3NDEKTSV4RRFFQ69G5FAV"

# NanoID
print(generate(size=21))
# → "V1StGXR8_Z5jdHi6B-myT"

Go

package main

import (
    "fmt"

    "github.com/google/uuid"     // UUID v4 & v7
    "github.com/oklog/ulid/v2"   // ULID
    gonanoid "github.com/matoous/go-nanoid/v2" // NanoID
)

func main() {
    // UUID v4
    fmt.Println(uuid.New())
    // → "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"

    // UUID v7
    fmt.Println(uuid.Must(uuid.NewV7()))
    // → "01906b5e-4a3e-7234-8f56-b8c12d4e5678"

    // ULID
    fmt.Println(ulid.Make())
    // → "01ARZ3NDEKTSV4RRFFQ69G5FAV"

    // NanoID
    id, _ := gonanoid.New()
    fmt.Println(id)
    // → "V1StGXR8_Z5jdHi6B-myT"
}

UUID v4からv7への移行

システムで既にUUID v4をプライマリキーとして使用していて、v7のパフォーマンスメリットを得たい場合、朗報があります。v4とv7は同じ128ビット形式を共有し、同じ uuid カラム型に格納されるため、スキーマの変更は不要です。

移行戦略

  1. 新しいレコードにはv7を、既存レコードはv4のまま使用。 両者は同じカラム内で共存できます。クエリやJOINは完全に同一です。
  2. ID生成コードを更新 ― アプリケーション層で uuidv4()uuidv7() に置き換えます。
  3. 既存のv4 IDは書き換えない。 外部キー、外部参照、キャッシュされたURLが壊れます。
  4. インデックスのパフォーマンスをモニタリング。 v4/v7の比率がv7寄りになるにつれ、インデックスの断片化は徐々に減少します。

互換性チェック

-- v4とv7が同じuuidカラム内に共存
SELECT id, version FROM (
  SELECT id,
    CASE get_byte(id::bytea, 6) >> 4
      WHEN 4 THEN 'v4'
      WHEN 7 THEN 'v7'
      ELSE 'other'
    END AS version
  FROM your_table
) t
GROUP BY version;

よくある質問

UUID v7と自動インクリメント整数、どちらを使うべき?

自動インクリメント整数はよりシンプルでコンパクト(4〜8バイト vs 16バイト)ですが、中央集権的なシーケンスが必要で、データベースでしか生成できません。UUID v7はクライアント、エッジ、マイクロサービスなど、どこでもデータベースへのラウンドトリップなしに生成できます。単一データベースのシンプルなアプリには自動インクリメント、分散システム、マルチテナントアーキテクチャ、またはクライアントサイドでのID生成が必要な場面にはUUID v7を使いましょう。

UUID v7の74ビットのランダム性で十分か?

十分です。74ビットのランダム性はミリ秒あたり2⁷⁴(約1.9 x 10²²)の可能な値を提供します。ミリ秒ごとに100万IDを生成しても、衝突確率は約10⁻¹⁰で、実用上問題になることはありません。UUID v4の122ビットのランダム性は、ほとんどのアプリケーションでは過剰です。

UUID v7からタイムスタンプを抽出できる?

はい。最初の48ビットにUnixミリ秒タイムスタンプがエンコードされています。

function extractTimestamp(uuidv7) {
  const hex = uuidv7.replace(/-/g, "").slice(0, 12);
  const ms = parseInt(hex, 16);
  return new Date(ms);
}

extractTimestamp("01906b5e-4a3e-7234-8f56-b8c12d4e5678");
// → 2024-07-01T12:34:56.000Z

これはバグではなく仕様です。ただし、不透明なIDが必要な場合はv4を使ってください。

PostgreSQL 18はUUID v7をネイティブサポートしている?

PostgreSQL 18(2025年リリース)には組み込みの uuidv7() 関数が追加され、pgcryptopg_uuidv7 などの拡張が不要になりました。MySQLにはまだネイティブのv7生成機能がないため、アプリケーション層で生成してください。

ULIDではだめなのか?

ULIDはUUID v7より前に登場し、同じ問題を解決しています。v7がIETF標準(RFC 9562)となった現在、v7には重要な優位性があります:ネイティブの uuid データベース型(16バイト、効率的なインデックス)、幅広い言語・フレームワークのサポート、正式な標準化です。既にULIDを使っているシステムはそのまま問題ありません。新規プロジェクトにはUUID v7を推奨します。

Snowflake IDの方が適しているケースとは?

ノードあたり毎秒10万以上の極めて高いスループットでコンパクトな64ビットIDが必要で、かつワーカーID割り当てのインフラが既にある場合です。Snowflakeの8バイト BIGINT ストレージはUUIDの半分で、数十億行規模ではこの差が重要になります。トレードオフは運用の複雑さで、ワーカーIDの割り当て管理とクロックスキューへの対応が必要です。


今すぐUUIDを生成したい方へ。UUID生成ツールをお試しください。v1、v4、v5、v7のバッチ生成とデコードに対応し、100%ブラウザ上で動作します。