1️⃣
IDをどうやって採番するか
IDをどうやって採番するか
1. はじめに
記事の目的
- Webアプリケーション開発で登場する、ドメインオブジェクトや集約(Aggregate)がもつ一意なIDの採番方法について紹介します。
- どこで採番するか(DB・アプリケーションなど)や、採番の形式(連番・UUID・ULIDなど)について、メリット・デメリット、適した状況を考察します。
読者想定
- これからアプリケーション設計を行う際に「IDをどこで・どうやって採番すればよいか」悩んでいるエンジニアやアーキテクト。
2. 集約(Aggregate)がもつIDの役割と重要性
-
IDの役割
- データの永続化や参照時に確実に一意性を担保する
- 外部システムや他の集約との連携での“共通のキー”として利用する
-
ID設計を誤った場合の影響
- 一意性が破壊された場合の重大なデータ不整合
- 大量トラフィックによるパフォーマンス低下
- 複数システム間でID管理が煩雑になり、保守が難しくなる
3. 採番方式の分類
3.1 どこで採番するか
-
データベースで採番する
- オートインクリメント(自動連番)
- シーケンス(Sequence)
- IDENTITYカラムなど
-
アプリケーションで採番する
- UUIDやULIDなどランダム/時系列のID生成機能を利用
- 独自のアルゴリズムを実装
-
外部サービス/別プロセスで採番する
- 分散ID生成システムや外部サービス
- 例: 分散ID生成ツールを独立させて運用
- セントラルなID発行機能を持つサービスを活用
- 分散ID生成システムや外部サービス
3.2 どのようなID形式を使うか
-
連番(自動インクリメント)
- 例: 1, 2, 3, …
- RDBのオートインクリメントやシーケンス機能を活用
-
UUID(ランダム)
- 例:
550e8400-e29b-41d4-a716-446655440000
- バージョンやランダム種別により細分化(e.g. UUID v4)
- 例:
-
タイムベース・ソート可能ID (ULID など)
- ULID例:
01ARZ3NDEKTSV4RRFFQ69G5FAV
- 特徴: 時系列が維持される(ソート可能)
- ULID例:
-
その他独自形式
- 例:
ORDER-20230112-123456
のように業務ドメインに合わせたID - 前半が日付、後半が連番、などのハイブリッド形式
- 例:
4. それぞれのメリット・デメリット
4.1 データベースで採番するケース
観点 | メリット | デメリット |
---|---|---|
実装の容易性 | RDBの標準機能(オートインクリメントやシーケンス)を使うだけなのでアプリ側の実装がシンプル | DB機能に依存するため、DB間の切り替えやマイクロサービスに広げる際に面倒 |
一意性の保証 | DBが一意性を担保するので衝突のリスクは少ない | 分散環境下で複数DBを利用する場合は一意性を担保しにくい |
パフォーマンス | 単一DBであれば高速に採番できる | 大量トラフィックがある場合はDBがボトルネックになりやすい。スケールアウト(シャーディング)が必要になると複雑化 |
可読性 | 連番なので人間にもわかりやすい | 連番だと「いま何件目か」が推測されやすく、セキュリティ面で懸念がある場合も。番号を飛ばす可能性があるなど細かい問題が発生することも |
監査・分析 | 連番なのでID順でカウントや時系列をある程度把握しやすい | 適切な管理ができないとオートインクリメントの上限に達する可能性がある。特にINT上限を超えるケースなどに注意 |
4.2 アプリケーションで採番するケース
観点 | メリット | デメリット |
---|---|---|
実装の容易性 | UUIDやULIDのライブラリ・標準APIを利用するだけなら簡単 | 設計時に衝突リスクや分散性を考慮する必要がある |
一意性の保証 | UUID(v4)などを利用すれば衝突確率が非常に低く、DBに依存しない一意性を実現 | 理論上0ではないものの、ほぼ誤差として扱われるケースが多い |
パフォーマンス | 採番のためにDBに問い合わせる必要がなく、DB負荷を軽減できる | アプリサーバーがスケールアウトした場合、時刻同期によるID生成順序のずれなどに注意が必要 |
可読性 | UUIDやULIDはランダム(もしくは時系列エンコード)な値を含むため、一目で順序がわからずセキュリティ的にメリットがある | 連番のようにどれが新しいのか人間が直感的に把握しにくい |
監査・分析 | ULIDであれば時刻情報が含まれているため、IDでソートすればおおよその作成順がわかる | 純粋なランダムUUIDの場合、時系列分析がしにくい |
4.3 外部サービス/別プロセスで採番するケース
観点 | メリット | デメリット |
---|---|---|
実装の容易性 | 分散ID生成ツールやサービスを使うだけなら比較的容易 | 新たなインフラ構築、運用コストがかかる |
一意性の保証 | 分散環境でも衝突しにくく、大量発行に耐えられる | ネットワーク障害が起こるとIDが取得できないリスク |
パフォーマンス | 高トラフィックでもスケールしやすい | 依存先のID生成サービスがダウンすると採番できない |
可読性 | (実装やサービスによるが) ランダムや時系列エンコードなど、多様な形式を選択可能 | 連番ほどではなく、人間にとってはやはり無機質 |
監査・分析 | 時刻情報が含まれるIDを生成できる場合、IDソートで大まかな時系列がわかる | 外部サービスの監査ログや生成履歴の管理が別途必要 |
5. 採番方式の選定基準
-
システム規模
- 小~中規模: 単一DBで十分な場合はDB連番がシンプル
- 大規模・分散: アプリケーション側や外部サービスで生成し、DBの負荷を下げる
-
IDの形式要件(可読性・規則性・サイズなど)
- 人間が入力・確認をする必要があるか → 短くて連番に近い形式の方が扱いやすい
- 時系列情報が欲しいか → ULIDなどを検討
-
開発・運用コスト
- 新たなインフラを導入できるか(外部サービス・分散ID生成システム)
- フレームワークやライブラリのサポート状況
-
既存システムとの連携
- 既存DBにオートインクリメントが設定されている場合 → 変更によるコスト・リスク
- 他システムがUUID形式で受け取ることを前提とする場合 → UUIDで統一
6. パターン別の具体例
6.1 小規模・モノリシックで管理するECサイト
- 状況: 単一のRDB(例えばMySQL)にすべてのテーブルが存在
-
採番例: 主キーはすべてオートインクリメントを利用
- 例:
orders
テーブルの主キーはorder_id (INT AUTO_INCREMENT)
- 例:
-
注意点:
- トラフィック増大やテーブル拡張の際にオートインクリメント上限問題が起こり得る
- シャーディングや複数DBになると整合性が難しくなる
6.2 マイクロサービス構成のシステム
- 状況: 複数のマイクロサービスが独立したDBを持ち、イベント連携を行う
-
採番例:
- サービス単位でUUID/ULIDを採番(アプリ側で生成)
- グローバルに一意である必要がある場合は、分散ID生成ツールや外部サービスも選択肢
-
注意点:
- オートインクリメントがサービス間でズレると衝突リスクが高い
- UUID/ULIDは可読性が低いが衝突はほぼ起こらない
- 時系列分析が必要ならULIDなどが便利
6.3 大規模・高トラフィック+分散環境
- 状況: 複数リージョンやデータセンターを持ち、大量ユーザが利用するアプリ
-
採番例:
- 専用のID生成サーバー(独立した分散ID生成機能)を用いて一意IDを発行
- 大量のIDを高速に衝突なく生成可能
-
注意点:
- ID生成のために専用サーバー・サービスを運用する必要がある
- 障害時のフォールバック策や時刻同期が重要
6.4 独自フォーマットが要求される業務アプリ
- 状況: 業務上、IDに日付や店舗コード、連番を含める必要がある
-
採番例:
-
yyyyMMdd + branchCode + serialNumber
のような複合キー - DBシーケンスや分散ロックで連番を管理し、アプリで文字列を組み立てる
-
-
注意点:
- フォーマット変更が発生する場合に備えて疎結合な設計を
- 連番部分での衝突が起こりやすいので、確実な排他制御が必要
7. まとめ
-
一意のIDの採番はアプリケーション設計の重要な要素
- 連番・UUID・ULIDなど、それぞれに一長一短がある
-
選定時のポイント
- システム規模、可用性、運用コスト、可読性、時系列分析の必要性など
-
実際の運用で注意すべきこと
- スケールアウト(単一DBがボトルネックにならない設計)
- 分散環境での一意性確保と時刻同期
- ドメイン固有の要件によるフォーマット指定がある場合の工夫
8. 今後の展望と参考情報
-
今後の展望
- 分散システムが一般的になる中で、IDのグローバル一意性を確保しつつ、可観測性(時系列の把握)を高める手法が重要になる
- 小規模システムや管理範囲が明確な場合は、シンプルなDB連番が運用コストを抑える有力な選択肢となる
-
参考情報
Discussion