← 一覧へ
データベース
#UUID#ULID#SnowflakeID#분산ID#기본키전략
最終更新 · 2026-10-07

分散一意識別子の生成戦略(UUID・ULID・Snowflake ID)

1. 概要

A. 定義

分散一意識別子(Distributed Unique ID)とは、複数のノード・サービス・データセンターが同時にレコードを生成する環境において、中央集中型の採番機に依存せずにグローバルな一意性(global uniqueness)を保証するよう設計された識別子生成技術である。代表的には乱数ベースのUUID、時刻でソート可能なULID・UUIDv7、ビット組み合わせベースのSnowflake IDがある。

分散一意識別子は、「新しく作るデータにどの主キー(Primary Key)を付与するか」というデータベース設計の最も基礎的な問いが、単一DBの時代を越えてマイクロサービス・シャーディング・マルチリージョン環境へ移るにつれ、再び難しい問題として浮上したところから出発する。かつてはRDBMSのAUTO_INCREMENTやシーケンスオブジェクトが事実上の標準であったが、水平分割された数十のシャードと地理的に分散した書き込みノードが同時に挿入を行う今日では、単一の採番地点そのものがボトルネックであり単一障害点(SPOF)となる。したがって識別子生成戦略は単なる実装の細部ではなく、拡張性・可用性・インデックス性能・セキュリティを左右するアーキテクチャ上の意思決定として扱われる。

B. 登場の背景と必要性

最も馴染みのある採番方式であるDBの自動増分キーは、単一ノード環境では簡潔でインデックス局所性にも優れる。しかしシャーディングを導入した瞬間に致命的な限界が現れる。シャードAとシャードBがそれぞれ1から番号を付けるとキーが衝突し、これを避けるために中央シーケンスサーバーを置くとすべての挿入が一地点を経由するため、スループットがそのサーバーの限界に縛られ、ネットワーク往復遅延まで加わる。マルチリージョンへ拡張するとリージョン間の調整コストが急増し、書き込み遅延が数十ミリ秒単位に膨らむ。

この問題は技術士試験において「MSA・大規模分散システムのデータ識別子設計」や「シャーディング環境の主キー戦略」として変形されて頻出し、単純な暗記ではなくトレードオフを説明する論述が求められる。したがって各方式の長所と短所を、一意性保証メカニズムから解き明かして説明できなければならない。

さらに自動増分キーは予測可能性というセキュリティ上の弱点を持つ。/orders/1001の次が/orders/1002であることは自明であるため、権限検証が甘ければ識別子を順番に変えて他人の資源を照会するIDOR(安全でない直接オブジェクト参照)攻撃に晒され、競合他社が「今月の注文番号 - 先月の注文番号」で営業規模を推定する列挙(enumeration)ベースの情報漏洩も可能になる。分散一意識別子はこのように ① 調整なしの一意性、② 高い生成スループット、③(必要に応じて)非予測性という三つの要求を同時に満たすために登場し、さらに ④ 時刻ソート性(index locality)まで加えることが最新設計の核心的課題である。

2. 識別子生成方式の分類と要求事項

分散ID設計は「一意性をどう確保するか」を基準に大きく三つの系統に分かれる。第一は乱数/ハッシュベースで、十分に大きな乱数空間を使って衝突確率を無視可能な水準まで下げるUUIDv4が代表的である。第二は時刻 + 乱数の結合で、上位ビットにミリ秒タイムスタンプを置いておおよその生成順にソートされるようにし、下位ビットは乱数で埋めるULID・UUIDv7である。第三は時刻 + ノード + 連番の組み合わせで、各ノードに固有番号を与えて調整なしに一意性を得るSnowflake系である。

flowchart TB
    Root["分散一意識別子の生成戦略"]
    Root --> A["乱数/ハッシュベース(調整なし)"]
    Root --> B["時刻+乱数(ソート可能)"]
    Root --> C["時刻+ノード+連番(準調整)"]
    A --> A1["UUIDv4(122bit 乱数)"]
    A --> A2["UUIDv1(時刻+MAC)"]
    B --> B1["ULID(48bit 時刻+80bit 乱数)"]
    B --> B2["UUIDv7(RFC 9562)"]
    C --> C1["Twitter Snowflake(64bit)"]
    C --> C2["Instagram/Sonyflake 変種"]

上の分類で各枝が分かれる根本的な理由は、一意性保証メカニズムの違いである。乱数ベースは「確率的にほとんど重ならない」という統計的保証に、組み合わせベースは「ノード番号が異なれば絶対に重ならない」という構造的保証に依存する。この違いがそのままノード調整の必要性、キー長、ソート可能性の違いへとつながる。

ここで「調整(coordination)」の性格を区別しておくと設計判断が明確になる。UUIDv4・ULID・UUIDv7は生成のたびにどのノードとも通信しない完全な調整なしであり、オフライン・エッジ・クライアント側の生成が自由である。Snowflakeは平常の生成では通信がないが、起動時にワーカーIDを受け取る一度きりの調整が必要である。一方DBシーケンス・中央採番サーバーは発番のたびに調整が起こる方式である。調整頻度が低いほど可用性とスループットは良くなるが、その分一意性を他の手段(乱数空間・ノード番号)で支えなければならないため、キーが長くなるかビット設計が複雑になる。この「調整頻度 ↔ キー複雑度」の交換が分散ID設計の軸をなす。

良い分散IDが備えるべき要求事項を整理すると次のとおりであり、これらは相互にトレードオフの関係にあって一つの方式ですべてを満たすのは難しい。

要求事項 意味 特に重要な状況
グローバル一意性 すべてのノード・時刻にわたり衝突なし シャーディング・マルチリージョン書き込み
生成スループット 秒あたり生成可能なID数 大量注文・ログ・メッセージ
時刻ソート性 生成順におおよそ増加 B木インデックス挿入性能
非予測性 次の値を推測できない 公開URL・セキュリティ識別子
緻密性・長さ 格納・転送コスト インデックスサイズ・ネットワーク

3. UUIDとULID — 調整なし(coordination-free)の方式

A. UUIDのバージョン別特性

UUID(Universally Unique Identifier)は128ビット値であり、通常は32個の16進数と4個のハイフンを合わせた36文字の文字列で表記する。2005年にRFC 4122として標準化され、2024年5月にRFC 9562がこれを改訂・置換しながら新バージョンを追加した。最も広く使われるUUIDv4は、バージョン・バリアント識別用の6ビットを除く122ビットを乱数で埋める。この空間は約5.3×10³⁶個に達し、秒あたり10億個ずつ85年生成しても一度の衝突が発生する確率はきわめて低い水準である。ノード間のいかなる通信も不要であるため調整コストが0であり、オフラインクライアントでも即座に生成できる点が決定的な長所である。

UUIDv4が広く使われるもう一つの理由はエコシステムの成熟である。ほぼすべての言語の標準ライブラリとデータベースがネイティブのUUID型と生成関数を提供するため追加のインフラなしに即座に導入でき、分散トレース(trace ID)・冪等性キー・イベントIDのような「短命で調整が不可能な」識別子に特によく合う。要するに性能が極端に重要な大容量主キーでないかぎり、v4は依然として最も安全で無難な既定の選択肢である。

一方UUIDv1は100ナノ秒単位のタイムスタンプとネットワークカードのMACアドレスを結合する。時刻情報が入るため理論上はソート可能性があるが、ビット配置が上位から時刻順でないため文字列ソートでは生成順が表れず、MACアドレスの露出により生成機器を特定できるというプライバシー問題が指摘されてきた(過去のメリッサウイルスの追跡事例が代表的である)。このため公開識別子にはv4が、追跡可能性が不要な内部用途には慎重にv1が使われる。

衝突確率をもう少し具体的に見ると、UUIDv4の一意性は誕生日のパラドックス(birthday problem)で測る。122ビット空間で衝突が50%の確率で初めて発生するにはおよそ2^61個、すなわち約2.3×10¹⁸個を生成しなければならない。これは全世界が数百年にわたり爆発的にIDを打ち出しても到達しがたい規模であるため、実務ではUUIDv4の衝突は「発生しない」とみなして設計する。ただしこの保証は乱数源(entropy source)の品質に全面的に依存するため、弱い疑似乱数生成器や仮想化環境の起動直後のエントロピー不足状態で生成すると実際の衝突リスクが急騰する点には必ず留意しなければならない。

UUIDv4の隠れた弱点はインデックス局所性の欠如である。値が完全な乱数であるためB木インデックスに挿入される際に毎回ツリーの無作為な位置に割り込み、ページ分割(page split)とバッファキャッシュミスを引き起こす。MySQL InnoDBのようにクラスタ化インデックスで主キーが物理的な並びを決めるエンジンでは、ランダムUUID主キーが挿入性能とキャッシュヒット率を大きく下げることが実測でよく知られており、大容量テーブルでは忌避されるか、後述するソート可能な方式へ置き換えられる。数千万件規模のテーブルでランダムUUID主キーが順次キーに比べ挿入スループットを数倍落としたというベンチマークが多数報告されており、この問題は理論ではなく現場の実質的なボトルネックとして扱われる。

B. ULIDとUUIDv7 — 時刻でソート可能なID

このインデックス問題を狙って登場したのがULID(Universally Unique Lexicographically Sortable Identifier)である。ULIDは128ビットを上位48ビットのミリ秒タイムスタンプ + 下位80ビットの乱数で構成し、これをCrockford Base32でエンコードして26文字の文字列で表現する。上位に時刻が来るため文字列を辞書順にソートすればそのまま生成時刻順になり、同じミリ秒内では80ビットの乱数で一意性を確保する。その結果、UUIDv4の調整なし生成という長所を保ちながら、インデックスにほぼ単調増加の形で挿入されページ分割が激減する。

RFC 9562が新たに導入したUUIDv7は、ULIDと実質的に同じ設計思想を標準UUIDフォーマットの中で実装したもので、上位48ビットにUnixミリ秒タイムスタンプを置き残りを乱数で埋める。既存のUUID格納・ライブラリのエコシステムをそのまま使いながら時刻ソート性を得られるため、新規設計ではv4の既定の代替として急速に定着しつつある。ただし時刻情報が露出するため「いつ作られたか」を隠すべきセキュリティ識別子には適さず、この場合は依然として完全乱数のv4が選ばれる。すなわちソート性と非予測性は同時に最大化できないトレードオフであり、用途に応じてバージョンを選ばなければならない。

ULID・UUIDv7のような時刻ソートIDにも注意すべき落とし穴がある。同じミリ秒内で生成されたID同士の相互順序は下位の乱数によって決まるため、ミリ秒未満の生成順は保証されない。厳密な単調増加が必要ならば、ULIDの「monotonicモード」のように同じミリ秒内では乱数の代わりに直前の値に1を足す変種を使わなければならない。また48ビットのミリ秒タイムスタンプは約8900年を表現できるため寿命面の心配は事実上ないが、時刻ソート性がクライアントの時計に依存する点は分散生成時の弱い環である。異なる機器で生成されたID同士のグローバルな順序は各機器の時計の精度の分だけしか信頼できないため、ソートをビジネスロジックの強い前提としてはならない。

4. Snowflake ID — 中央調整ベースの64ビットID

A. 64ビットのビット構造

Snowflake IDは2010年にTwitterが分散環境でソート可能でありながら64ビットで緻密な識別子を必要として考案した方式である。128ビットUUIDは長くインデックス・ネットワークコストが大きく、DBシーケンスは中央ボトルネックであるという二つの問題を同時に解くために、64ビット整数を以下のように四つの領域に分割する。

flowchart LR
    S["符号 1bit(常に0)"] --> T["タイムスタンプ 41bit(ms, 約69年)"]
    T --> W["ワーカーID 10bit(ノード1024個)"]
    W --> Q["シーケンス 12bit(msあたり4096個)"]

上位から符号ビット1個(正数保証用の0)、タイムスタンプ41ビット、ワーカーID10ビット、シーケンス12ビットで構成される。41ビットのミリ秒タイムスタンプは基準時点(epoch)から約2^41ms、すなわち約69.7年を表現でき、サービス開始時点をカスタムepochに設定すれば数十年をカバーする。ワーカーID10ビットは最大1024個のノードを区別し、シーケンス12ビットは一つのノードが同じミリ秒内に最大4096個(2^12)のIDを発番できるようにする。結果として理論上はシステム全体でミリ秒あたり1024×4096 ≈ 419万個、秒あたり約42億個のIDを調整なしに生成できる。

B. 生成手順と障害対応

生成過程は次のように動作する。ノードは現在時刻を読み取ってタイムスタンプフィールドを埋め、直前の発番と同じミリ秒ならばシーケンスを1増やし、シーケンスが4096を超えると次のミリ秒になるまで短く待機(busy-wait)する。ミリ秒が変わればシーケンスを0にリセットする。上位ビットに時刻があるため生成されたIDはグローバルにおおよそ時刻順に増加し、同じノード内では完全に単調増加である。このおかげでULIDのようにインデックス局所性が良いうえ、64ビットでUUIDの半分のサイズという利点を持つ。

Snowflakeの核心的前提はワーカーIDの一意性と時計の単調性である。二つのノードが同じワーカーIDを使うと衝突するため、ノード起動時にZooKeeper・etcd・DBなどから重複のないワーカーIDを割り当てられなければならない。この点が完全に調整なしのUUIDと異なり起動時点の軽量な調整を要求する部分であるため「準調整(semi-coordinated)」方式に分類される。またNTPの時計逆行やうるう秒でタイムスタンプが後ろに戻ると重複が生じ得るため、実務の実装は最後の発番時刻より時計が遅れたらID発番を止めるか誤差範囲内で待機する防御ロジックを置く。

C. ビット配分の柔軟性と運用環境

ビット配分そのものが設計パラメータである点も重要である。タイムスタンプ・ワーカー・シーケンスに何ビットを割り当てるかは「寿命 vs ノード数 vs 秒あたり発番量」のトレードオフを反映した選択であり、Twitterの10/12配分は「ノードは1024個で十分でノードあたりの発番量を最大化」した均衡点である。逆にノードが数万個の環境ではワーカービットを増やしシーケンスビットを減らすか、時刻解像度を下げる形で再配分しなければならない。すなわちSnowflakeは単一の公式ではなく、組織のトラフィックプロファイルに合わせてビット幅を調整する設計の枠組みとして理解するのが正確である。Kubernetesのようにノードが頻繁に生成・消滅する環境では固定ワーカーID割り当てが難しいため、ワーカーIDをシーケンス空間から動的に借りたりポッド情報をハッシュして付与する変種、あるいは中央セグメント割当器(例: Leaf-segment)と結合するハイブリッド設計が併せて検討される。

5. 比較と事例

A. 方式別の特性比較

三つの方式の選択は「何を諦めるか」の問題である。以下の比較に見るとおり、完全な調整なしと緻密な64ビットソート性を同時に持つことはできない。

区分 UUIDv4 ULID / UUIDv7 Snowflake
長さ 128bit / 36文字 128bit / 26文字(ULID) 64bit
一意性保証 確率的(乱数) 確率的(時刻+乱数) 構造的(ノード+連番)
ソート性 なし 時刻順ソート 時刻順ソート
ノード調整 不要 不要 起動時にワーカーID必要
非予測性 非常に高い 低い(時刻露出) 低い(時刻・ノード露出)
時計依存 なし あり 強い(逆行防御が必要)

実務事例はこのトレードオフをよく示す。第一に、Instagramは初期のシャーディング環境で64ビットIDをPostgreSQLのストアドプロシージャで生成したが、上位41ビットの時刻、中間13ビットの論理シャードID、下位10ビットのシャード別シーケンスの組み合わせで設計し、「IDを見ればどのシャードにあるか分かる」ルーティング上の利点まで得た。これはSnowflakeの思想をシャード識別に応用した代表例である。第二に、DiscordはTwitterのSnowflakeを採用しつつ2015-01-01をカスタムepochとし、メッセージIDの時刻ソート性を用いて「特定時刻の前/後のメッセージ」を別途のインデックスなしにID範囲クエリでページングする。第三に、ソニー(Sonyflake)はノード数を2^16個まで増やす代わりに時刻解像度を10ms単位に下げて約174年を表現するようにビット配分を再設計したが、これは「ノードが非常に多く秒あたりの発番量は相対的に少ない」環境に合わせた変種である。国内外の多くのサービスがBaidu UidGenerator、Meituan LeafのようにSnowflakeをセグメント割当・時計補正と結合した社内ライブラリを運用しているのも同じ文脈である。

第四に、MongoDBのObjectIdは、この三系統の設計思想が一つの識別子に溶け込んだ興味深い折衷の事例である。12バイト(96ビット)で構成され、上位4バイトは秒単位のUnixタイムスタンプ、中間5バイトはプロセス・マシンを区別する乱数、下位3バイトは増加カウンターである。上位に時刻があるためおおよその時刻ソートになり(ULID・Snowflakeの長所)、中間の乱数でノード調整なしに異なるインスタンス間の衝突を避け(UUIDの長所)、下位カウンターで同じ秒内の一意性を保証する。128ビットUUIDより短く64ビットSnowflakeより長いが、中央調整が全くない点で「完全な調整なし + 時刻ソート」を96ビット内で実現した実用的な設計として広く使われる。このように実システムの識別子はたいてい一つの純粋な型ではなく、複数の思想の混合であることを示している。

6. 深化 — インデックス局所性とUUIDv7標準化の動向

最近の識別子設計で最も熱い争点はデータベースのインデックス局所性である。書き込みの多い大容量テーブルでランダムなUUIDv4主キーはB木の無作為な地点に挿入を起こし、ディスク書き込み増幅とキャッシュミスでTPSを目に見えて下げる。逆に単調増加キーはインデックスの右端にのみ挿入されキャッシュ効率が高いが、複数ノードが同じホットページに集中してロック競合(hotspot)を引き起こし得る。そこで設計の均衡点は「完全ランダムでも完全順次でもない、時刻接頭辞 + 乱数の末尾」であり、これがULIDとUUIDv7が注目される技術的根拠である。

この争点をめぐりUUID・Snowflake以外にも様々な変種が提案されてきた。KSUIDは32ビットの秒単位タイムスタンプと128ビットの乱数を合わせて160ビットにしBase62でエンコードしてURLに親和的でありながら時刻ソートになるようにし、NanoIDは一意性より短くURL安全な文字列に焦点を当てた乱数IDでフロントエンド・短縮URLの領域で人気を集める。これらはいずれも「時刻接頭辞 + 十分な乱数」という共通の骨格を共有しつつ、長さ・エンコード・ソート保証の水準で互いに異なる地点を選んだものである。すなわち識別子設計はいくつかの軸(ソート性・長さ・非予測性・調整頻度)の上で座標を選ぶ問題であり、どのライブラリを選ぶにせよその座標が自らの要求に合うかをまず吟味しなければならない。

運用観点の深化した争点として識別子生成そのものの可観測性(observability)も外せない。Snowflakeノードの時計が逆行してID発番が止まったり、特定のミリ秒でシーケンス4096個が枯渇して待機が長引けば、それ自体が遅延急増の原因になる。したがって成熟した運用組織はノード別の発番QPS、シーケンス飽和回数、時計逆行の検知イベント、ワーカーID割当の衝突を指標として収集し早期警報する。またカスタムepochが41ビットを枯渇させる時点(サービス開始後約69年)は当面は遠く見えても「いつか必ず来る」設計上の負債であるため、設計文書に枯渇年を明記しておくのが責任ある設計態度である。

標準化の面では、2024年5月に発表されたRFC 9562は既存のRFC 4122を置換し、UUIDv6(v1を並べ替えてソート可能に)・v7(Unix時刻ベース)・v8(カスタム)を公式化した。これはこれまでULID・KSUID・Snowflakeなどの非標準が乱立していた「時刻ソート可能ID」の領域を標準UUID体系へ吸収しようとする流れであり、PostgreSQL・主要なORM・言語の標準ライブラリがUUIDv7のサポートを急速に追加している。技術士の観点では「今後新規の分散システムの主キーの既定値がv4からv7へ移るのか、そして64ビットの緻密性が重要な領域ではSnowflakeが共存し続けるのか」が核心的な展望である。あわせて公開APIの識別子は時刻・ノード情報が露出しないようv4を使うか、内部のSnowflakeをハッシュ化(HMAC)した公開トークンやHashidsでもう一度包んで列挙攻撃を遮断する設計がセキュリティのベストプラクティスとして定着しつつある。

7. 考慮事項および示唆点

技術士の観点から分散一意識別子戦略を選択・導入するときは、次を総合的に検討しなければならない。

  • 用途別の複数戦略採択: 一つの方式ですべての要求を満たせないため、内部主キーはインデックス局所性の良いSnowflake・UUIDv7で、外部に露出する公開識別子は非予測性の高いUUIDv4または別途のトークンで分離して設計するのが現実的である。一つのエンティティが内部キーと公開キーを併せ持つ二重識別子パターンを積極的に検討する。
  • インデックス・ストレージコストのトレードオフ: 128ビットUUIDは64ビットSnowflakeに比べ主キー・すべての外部キー・インデックスで二倍の空間を使い、結合・ネットワークコストも大きくなる。レコード数が数十億件に達するテーブルならば64ビットの緻密性の利点が相当であるため、一意性保証方式と長さを併せて秤にかけなければならない。
  • 時計の信頼性と障害対応: Snowflake・ULID・UUIDv7はいずれもシステム時計に依存するため、NTP同期の品質、時計逆行の防御、カスタムepoch枯渇(41ビット消尽)の時点を運用設計に反映しなければならない。ワーカーID割当体系(ZooKeeper・etcd・DB)の可用性もID生成の前提条件であることを忘れてはならない。
  • セキュリティ・プライバシーへの影響評価: 識別子に含まれる時刻・ノード・MAC・シャード情報はそれ自体がメタデータ漏洩になり得る。IDOR・列挙攻撃の防御のために権限検証を識別子の非予測性だけに依存させないようにし、必要な場合は公開識別子を乱数化・ハッシュ化する。
  • 移行戦略: すでに自動増分キーで運用中のシステムを分散IDへ転換するときは、既存のキーを保ちながら新しい識別子カラムを並行して導入し参照を段階的に移管するストラングラー(strangler)方式が安全である。キー型の変更はすべての外部キーとアプリケーションの契約に波及するため、段階的な移行計画が必須である。
  • ソート・ページングの活用の両面性: 時刻ソートIDは「ID範囲クエリ」だけで時系列ページングを実装する強力な利点を与えるが、これを乱用してIDをソート・時刻比較の唯一の根拠とすると時計誤差・ミリ秒内の順序未保証の落とし穴にかかる。生成順がビジネス的に重要ならば別途の生成時刻カラムや論理的シーケンスを併せて置き、識別子と順序基準を分離する方が堅牢である。
  • 標準収束と長期互換性: RFC 9562で時刻ソートIDがUUID標準へ吸収される流れを考えると、新規システムは社内のカスタムフォーマットより標準UUIDv7の採択がエコシステム互換性と長期保守の面で有利になり得る。ただし64ビットの緻密性が決定的な領域ではSnowflake系が引き続き有効であるため、標準収束を盲目的に追うより格納コスト・ソート性・セキュリティ要求を再評価して選択する。

参考資料


一言まとめ: 分散一意識別子は中央採番機なしにグローバルな一意性を保証するための設計であり、完全に調整なしで非予測のUUIDv4、時刻ソートでインデックス局所性を得るULID・UUIDv7、64ビットの緻密性と準調整の一意性を提供するSnowflakeが代表的で、一意性保証方式・ソート性・長さ・セキュリティのトレードオフを用途別に分離して設計することが核心である。