sequencer について
sequencer は、分散システムの ID 生成方式を選ぶためのシステム設計スキルです。順序性、スループット、キー幅、情報漏えい、障害時のトレードオフを比較します。
主な機能
- 015種類の ID 生成方式の比較
- 02順序性と書き込み速度に応じた選定
- 03クロックスキューとカウンター超過の評価
- 04ビット配置、エンコード、ノード ID の計画
- 05シャード集中とアロケーターリスクの確認
活用シーン
- 01複数ノード書き込み用の主キー設計
- 02クライアント側やオフラインでの ID 生成評価
- 03高書き込み速度向けの小型 ID の計画
- 04時計の巻き戻りやノード ID 衝突の検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ proyecto26/ system-design-skills --skill sequencerインストール後、Claude Code で/sequencerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/deep-dive.mdreferences/providers/aws.mdgcp.mdgeneric.md詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
シーケンサー
中央のボトルネックを設けずに、すべてのノードで一意な識別子を発行します。また、その ID にソート可能性や単調性も必要かどうかを判断します。ここでの判断を誤ると、後になって深刻な問題が発生します。衝突はデータを破損させ、単一の採番器は書き込みスループットに上限を設け、作成時刻や連番が漏れる ID はビジネス上の秘密を明らかにし、列挙攻撃を可能にします。
どんなときに使うか
複数のノードにまたがって新しいレコード(注文、メッセージ、アップロード、イベント)を書き込み、それぞれに主キーが必要なシステムで使います。単一の自動採番カラムがすべての書き込みを直列化してしまう場合、DB への往復通信を行う前に ID を生成する必要がある場合(クライアント側、オフライン)、または別のソート用フィールドを設けずにレコードをおおむね時系列順に並べたい場合に使います。
使わないほうがよい場合
単一のリレーショナルノードで、書き込み負荷をまだ余裕をもって処理できる場合(→
back-of-the-envelope)は、単純な BIGINT AUTO_INCREMENT/SERIAL が最も低コストで適切な選択肢です。そのために分散 ID サービスを構築しないでください(YAGNI)。
自然に一意となるキー(メールアドレス、ISBN、コンテンツハッシュ)がすでにある場合は、それを使います。不変条件で本当に必要でない限り、グローバルな単調性を求めないでください。ここで最もコストのかかる特性であり、通常はエンティティ単位の順序付けで十分です。
まず確認すること
- 生成場所 — クライアント/エッジ、アプリサーバー、またはデータベースのどこですか?(ID ごとに DB への往復通信を許容できるかどうかが決まります。)
- 順序付けの要件 — 不要、時刻順にソート可能(k-sortable でよい)、または厳密な単調性のどれですか?エンティティ単位ですか、それともグローバルですか?これが最も重要な分岐点です。
- 書き込みレートとノード数 — ピーク時の ID 生成数/秒と、生成器の数(→
back-of-the-envelope)を確認します。シーケンスカウンターに必要なビット数が決まります。 - サイズとエンコードの予算 — 64 ビット整数(インデックス付きキーとして低コストで扱える)、128 ビット(調整が不要)、短い URL 安全な文字列のどれですか?
- 情報漏えいの許容度 — ID から作成時刻や推測可能な件数が分かってもよいですか(列挙攻撃/競合他社への情報提供)?
選択肢
- 自動採番/SQL シーケンス — 1 つの DB カラムから ID を発行します。単一ノードが書き込みを担当し、新たなインフラを一切増やしたくない場合に使います。
- UUIDv4(ランダムな 128 ビット) — どこでも生成でき、調整は不要で、衝突リスクは事実上ゼロです。一意性だけが必要で、ID によるソートが不要な場合に使います。
- ULID/UUIDv7(時刻を先頭に含む 128 ビット) — ランダム値にミリ秒単位のタイムスタンプを先頭に加えるため、ID を作成時刻順に並べられます。UUIDv4 の調整不要という特性と、時刻順の両方が必要な場合に使います(新しいキーでの現代的なデフォルトです)。
- Snowflake 方式(タイムスタンプ+ノード+シーケンス、64 ビット) — タイムスタンプ、ノード ID、ミリ秒ごとのカウンターをソート可能な 64 ビット整数に詰め込みます。コンパクトで k-sortable な整数キーが重要となる、高い書き込みレートで使います。
- DB チケット/範囲割り当て(Flickr 方式) — 中央のテーブルが ID のブロック(例:一度に 1000 個)を払い出し、各ノードはメモリ上のブロックから ID を発行します。ID ごとの調整なしで、単調性に近い整数をシンプルに使いたい場合に適しています。
トレードオフ
| 選択肢 | 解決する課題 | 悪化する点 | 切り替えるタイミング |
|---|---|---|---|
| オートインクリメント/シーケンス | 単純で、単調増加し、コンパクトな整数です | 書き込みが直列化されます。単一ノードではスループットに上限があります。件数が漏れます | 書き込み量が単一ノードの処理能力を超えた場合、またはクライアント側で ID を生成する必要がある場合 → チケット/Snowflake |
| UUIDv4(ランダム) | どこでも生成でき、調整が不要で、情報が漏れません | 128 ビットです。ランダムな順序でインデックスの局所性が損なわれます(ページ分割)。ソートできません | 時系列順が必要な場合 → ULID/UUIDv7 |
| ULID/UUIDv7 | 調整不要、時系列順にソート可能、インデックスに適しています | 依然として 128 ビットです。ミリ秒単位でしかソートできず(厳密な順序ではありません)、作成時刻が漏れます | 64 ビットのキーまたは厳密な順序が必要な場合 → Snowflake/シーケンス |
| Snowflake 方式(64 ビット) | コンパクトで、おおむねソート可能、ノードあたり約 400 万 ID/秒です | ノード ID の割り当てとクロックスキューへの対処が必要です。エポック/ビット配分によって有効期間に上限があります | クロック同期が信頼できない場合、またはノード ID を割り当てられない場合 → ULID |
| DB チケット/範囲 | おおむね単調増加する整数で、調整が少なく、シンプルです | 割り当てテーブルが単一障害点になります。再起動時に欠番が生じます。ノード間の順序は大まかです | 割り当て処理がボトルネックまたは単一障害点になった場合 → Snowflake/ULID |
高負荷時の挙動
分散 ID 方式の目的は、単一の割り当て元を避けることです。そのため、障害の原因は主に調整を簡略化する手法に集中します。
- **割り当て元が単一障害点/ボトルネックになる(チケット、単一シーケンス):**すべての書き込みが 1 つの行/ノードで停止します。負荷急増や障害が発生すると、すべての挿入が滞ります。*軽減策:*より大きな範囲を払い出す、割り当て元をレプリケートする、または Snowflake/ULID(中央へのアクセスが不要)に移行します。範囲を大きくすると単調増加性が損なわれ、再起動時に ID が無駄になります。
- **クロックスキューと時刻の巻き戻り(Snowflake/時刻プレフィックス方式):**ノードの時計が巻き戻ると(NTP 補正、VM の一時停止など)、すでに使用したタイムスタンプを再び発行し、ノード ID とシーケンスの範囲内で衝突する可能性があります。軽減策:
now < last_timestampの間は発行を拒否する(ブロックまたはエラー)、単調増加するクロックソースを使う、スキューをアラートで通知する。壁時計の時刻を決して無条件に信用しないでください。 - **シーケンスビットの枯渇:**1 つのノードで 1 ミリ秒あたり
2^seq_bitsを超える ID を発行すると、カウンターがオーバーフローします。*軽減策:*次のミリ秒までスピンウェイトするか、ピーク時のレートに合わせてビット配分をあらかじめ設定します。 - **ノード ID の衝突:**2 つのジェネレーターが同じノード ID で起動すると(設定ミス、自動スケーリング時の再利用など)、重複が気づかれないまま生成されます。*軽減策:*静的な設定ではなく、コーディネーターからノード ID のリースを取得します(→
consistency-coordination)。 - **連番キーによるホットシャード:**単調増加する ID をシャード/パーティションキーとして使うと、新しい書き込みがすべて 1 つのシャードに送られます。*軽減策:*キーをハッシュするか、プレフィックスでシャーディングします。パーティショニングに関する修正方法は
data-storageにあります。
**監視項目:**ノードごとの ID 発行レート、クロックスキュー/巻き戻りイベント、割り当てレイテンシと範囲枯渇率、重複キーエラー(ゼロであるべきです)。
適用方法
- 入力を明確にする — 生成場所、順序の要件(なし/時間順に並べ替え可能/厳密。エンティティ単位か全体か)、ピーク時の ID 数/秒、サイズの予算、情報漏えいの許容範囲を特定します(まず確認することを参照)。書き込みを処理する DB ノードが 1 台なら、そこで止めてください。自動採番を使います(→
back-of-the-envelope)。 - トレードオフ表から選ぶ — 順序は不要で 128 ビットで問題ない場合は UUIDv4、調整なしで時間順に並べ替えたい場合は ULID/UUIDv7、高レートでコンパクトな 64 ビットの k-sorted 整数が必要な場合は Snowflake、シンプルな単調増加整数が必要な場合はチケット/範囲を選びます。
- 主要なパラメーターを設定する — Snowflake では、ノード数とピークレートに合わせてエポックと timestamp/node/seq のビット分割を決めます。チケットではブロックサイズを選びます。ノード ID の割り当て方法(リースか固定設定か)を決め、エンコーディング(生の整数、Base62、Crockford Base32)を選択します。
- 負荷テストで選択を検証する — 高負荷時の挙動で、時計の巻き戻り、シーケンスの枯渇、ノード ID の衝突、アロケーターの障害、連番キーによるホットシャードの影響を確認します。想定するプロファイルで発生する項目それぞれに、対処策があることを確認します。
- 数値で見積もる — ビット予算がノードごとのピーク ID 数/秒をカバーし、エポックから十分な年数を確保できることを確認します。エンコード後の長さがキー/URL の制約を満たすことも確認します(→ 重要な数値)。
- プロバイダーを選ぶ — 汎用的な方法(Snowflake ライブラリ、チケット行、または ULID/UUIDv7)をデフォルトにします。ユーザーがクラウドを指定した場合に限り、プロバイダーファイルを開きます(プロバイダーの選択を参照)。
推奨事項と禁止事項
推奨事項
- 調整なしでおおよその時間順に並べたい新しいキーには、ULID/UUIDv7 をデフォルトで使います。UUIDv4 でランダムなインデックスが引き起こす問題を避けられます。
- Snowflake を選ぶ前に、ピークレートと必要な稼働期間に合わせてビット予算(timestamp/node/sequence)を見積もり、エポックを記録します。
- ノードが自動スケールする場合、固定設定ではなくコーディネーターからノード ID のリースを取得します。
- 時計が巻き戻った場合は ID の発行を拒否し、時刻のずれをアラートで通知します。重複キーエラーは P1 として扱います。
- 採番や情報漏えいが問題になる場合は、内部キー(ソート可能で、時刻が漏れる可能性がある)と、外部向けの不透明な ID を分けます。
禁止事項
- 数値で 1 台の DB ノードでは処理しきれないと示される前に、分散 ID サービスを構築しないでください(YAGNI)。
- ランダムな UUIDv4 を、負荷の高いテーブルのクラスタ化インデックス/主キーとして使わないでください。ランダムな順序はページ分割と書き込み増幅を引き起こします。
- レプリケーションなしで、単一のシーケンスやチケット行をフリート全体のアロケーターにしないでください。SPOF となり、書き込みのボトルネックになります。
- グローバルに単調増加する ID をシャードキーにしないでください。ホットシャードが発生します(
data-storageで対処してください)。 - 順序付けに壁時計の時刻を信頼しないでください。ミリ秒単位でソート可能な ID は k-sorted であり、厳密な順序ではありません。
重要な数値
64 ビットの Snowflake レイアウト(timestamp 約 41 ビット+node 10 ビット+sequence 12 ビット)は、エポックから約 69 年、1024 ノード、ノードあたりミリ秒間 4096 ID、つまり ノードあたり約 400 万 ID/秒を実現します。ほとんどの単一サービスには十分です。UUID/ULID は 128 ビット=16 バイト(64 ビット整数の 8 バイトに対して)で、インデックスキーのサイズが 2 倍になります。チケットのブロックサイズを N ID にすると、アロケーターへのアクセスは N 倍減りますが、ノードの再起動時に最大 N 個の ID が失われる可能性があります。ピークレートとストレージの見積もりについては、back-of-the-envelopeを参照してください。判断を左右する数値のみを再掲します。
インターフェースの概要
発行する ID は契約です。幅(64 ビットか 128 ビットか)、レイアウト(例:Snowflake [timestamp:41 | node:10 | seq:12])、順序の保証(順序なし/ミリ秒単位の k-sorted/厳密)、エンコーディング(生の整数、Base62、Crockford Base32。大文字と小文字を区別せず、URL セーフ)を明記します。ジェネレーターのエンドポイントがある場合も、最小限にします。next(entity) -> {id, issued_at}。ID がストレージキー、ソートキー、またはその両方のどれなのかを文書化します。この選択はdata-storageで使用されます。
プロバイダーの選択
まずは上記の汎用レシピを使用します。ユーザーがクラウドを指定した場合は、
references/providers/<provider>.md を読み、マネージドサービスとの対応関係、
クォータ/制限、プロバイダー固有のトレードオフを確認します。そのプロバイダー向けの
ファイルがない場合は、汎用レシピが回答になります(ほとんどのクラウドには専用の ID
サービスがありません。ライブラリを実行するか、独自にシーケンスを生成します)。
図
発行経路(生成ノード → 64 ビットレイアウト → レコードキー)または
チケットサーバーのブロック割り当てフローを視覚化するには、プラグイン内の
architecture-diagram スキルを使用します。ビット予算を手早く検討するなら、
インラインの [ts | node | seq] 図で十分です。Mermaid は埋め込まないでください。
関連する構成要素
data-storage— ここに情報を渡します。ID は主キー/ソートキーになり、連番キーによる ホットスポットを防ぐシャーディング/パーティショニングは、こちらで扱います。consistency-coordination— 単調性の保証とノード ID のリースを支える因果関係、順序付け、 リーダー選出の理論について、こちらに依存します。リンクを案内し、内容を再説明しないでください。messaging-streaming— メッセージの順序付けと重複排除において、組み合わせて使用します。 時系列順に並べられる ID によって、自然なシーケンスと冪等性の基準が得られます。api-design— 組み合わせて使用します。ID 生成は安全な再試行に使う冪等性キー(こちらで扱います)の基盤です。system-design— オーケストレーターで扱う概念です。推論ループ、トレードオフの検討手法、 10 個の障害モードが該当します。
参考資料
references/deep-dive.md— Snowflake のビットレイアウトの計算とエポックの選択、 クロックスキュー/単調時計への対応、チケットサーバーによる範囲割り当て、ULID と UUIDv7 のバイトレイアウト、エンコーディング(Base62/Crockford)、ノード ID のリースを扱います。 ジェネレーターを詳細に設計する際に読んでください。references/providers/{generic,aws,gcp}.md— 環境ごとのライブラリ/サービスとの対応関係、 制限、落とし穴を扱います。