DynamoDB には MySQL のような自動インクリメントの整数 ID がありません。
それに代わるものを探して見つけた以下の記事が大変興味深かったので、 拾い読みして自分用の覚え書きとしてまとめます。
Introducing Laravel ObjectId — The Fastest MongoDB-Style Identifier for Laravel Models | by Hamada Habib | Nov, 2025 | Medium
https://medium.com/@ihfbib/introducing-laravel-objectid-the-fastest-mongodb-style-identifier-for-laravel-models-3802814324d9
背景と目的
- 通常、Laravel のモデルではデフォルトで自動インクリメントの整数 ID を使うことが多い。だが、アプリが成長して分散システム/マイクロサービス化/API連携などを行うようになると、そのような整数 ID は制約になる場合がある。
- そこで、Laravel ObjectId というパッケージが登場。MongoDB の ObjectId に着想を得た、グローバルに一意かつ高速で生成できる識別子を、MySQL・MariaDB・PostgreSQL などの標準 SQL データベース上で使えるようにする仕組み。MongoDB を使っていなくても使える。
Laravel ObjectId の概要
12 バイトのコンパクトな ID
- ObjectId は 12 バイト(96 ビット)、16 進で表すと 24 文字の文字列。
- 内部構造は以下の通り:
- タイムスタンプ(4 バイト) UNIX epoch 秒 — 生成時間を含み、生成順ソート可能
- マシン識別子(5 バイト) ホストごとランダム・一意
- プロセス ID(2 バイト) プロセス識別
- カウンタ(3 バイト) プロセス内でのインクリメントカウンタ
- この構造により、集中管理なしで一意性を保証しつつ、生成時刻順のソート性も自然に持てる。
対応するデータベース & ライブラリ
- Laravel ObjectId は MySQL / MariaDB / PostgreSQL にネイティブに対応。MongoDB ドライバ/拡張は不要。
- コア PHP 実装のライブラリ wooserv/php-objectid に加えて、Laravel 向け統合パッケージ wooserv/laravel-objectid が用意されており、Eloquent モデルとの親和性が高い。
使い方(例)
use WooServ\LaravelObjectId\Concerns\HasObjectIds;
use Illuminate\Database\Eloquent\Model;
class Post extends Model {
use HasObjectIds;
}
- これで新規レコード作成時に、ID が自動で ObjectId に。
マイグレーションでは:
Schema::create('posts', function (Blueprint $table) {
$table->objectId(); // 24 文字の主キーカラムを作成
// ...
});
- また、どこでも objectid() ヘルパーで ID を生成可能。例:6730b6a0d8a28f890b7c9f40 のような文字列。
パフォーマンスと利点
記事ではベンチマーク結果を提示:
- 10,000 回の ID 生成ベンチマークでは、
- ObjectId:約 0.412 µs / ID
- UUID:1.283 µs / ID
- ULID:1.147 µs / ID → ObjectId が最速。約 3 倍速い。
- データベースへの挿入(1,000 レコード)では、
- ObjectId:合計約 14.78 ms
- UUID:15.48 ms
- ULID:15.17 ms → 実質的に差は小さいが、生成オーバーヘッドは ObjectId が有利。
比較表で示すと:
| 特性 | ObjectId | UUID | ULID |
|---|---|---|---|
| 文字列長 | 24 文字 | 36 文字 | 26 文字 |
| バイト長 | 12 | 16 | 16 |
| ソート可能(生成順) | ✅ タイムスタンプ付き | ❌ | ✅ |
| ランダム性 | ✅ | ✅ | ✅ |
| 対応 DB | MySQL/MariaDB/PostgreSQL | 同様 | 同様 |
| MongoDB 必要か | ❌ | ❌ | ❌ |
なぜ「ObjectId」が選ばれるか
- 自動割当・マイグレーション用マクロで導入が簡単。開発者にとって導入のハードルが低い。
- インデックスやストレージのサイズを抑えられ、データベースの効率性が向上。
- タイムスタンプを持つことで、生成順にソート可能 → ログ/イベント/分散トランザクションなど、順序が重要な用途で有利。
- MongoDB など NoSQL に移行する必要なく、既存の SQL DB をそのまま活かせる ― Laravel の既存アプリにも導入しやすい。
こんな状況に向いている
- Laravel を使っていて、将来的にマイクロサービスや複数 DB/複数インスタンス構成を考えている。
- UUID のような長くてインデックス負荷が高い識別子を使いたくない。
- レコードの生成時間順でソートが必要/便利なログ・イベント管理用途。
- MongoDB を導入せず、既存の MySQL / PostgreSQL をそのまま使いたい。
「文字列 ID が整数より有利だから」ではなく、 「ObjectId は分散環境でも衝突しない一意性と時系列ソート性を同時に持てる」という点がメリットです。
なぜ整数ではダメになる場面があるのか
| ID 方式 | 特徴 | 問題が起きる状況 |
|---|---|---|
| 整数(auto increment) | 小さく高速。シンプル。 | 1台のDBに依存 → 分散環境でIDの衝突リスク |
| UUID(v4) | 衝突ほぼなし | 長く重い、時系列順に並ばない、索引負荷↑ |
| ObjectId | 衝突回避+時系列ソート可能+比較的短い | NoSQL Mongo風の利点をSQLで使える |
ObjectIdの特に大きいメリットはこの2つ
① 生成した瞬間に一意性が保証される(DBに依存しない)
整数IDはこうなる:
DB が発番 → INSERT → ID決定
なので DBが1台で管理する必要がある。 スケールアウトやマイクロサービス化すると衝突が起きやすい。
ObjectIdなら:
DBに触らなくてもIDが生成できる(衝突ほぼなし)
- 複数サーバーが同時に INSERT しても安全
- 非同期バッチ・分散アプリ・キュー処理に強い
② IDを見ただけで「いつ生成されたかがわかる」
ObjectIdの先頭4byteはUNIXタイムスタンプ。
6730b6a0d8a28f890b7c9f40
↑ここがtimestamp → 生成順にソートできる
- orderBy(‘id’)だけで作成順に並ぶ
- ログ、イベント履歴、データストリームに向く
- UUIDにはこの特性がない
マシンごとに閉じたIDではなく、数マシン・複数プロセスが同時にIDを生成しても重複しない。 これは DBに依存せず自律的に生成できる点が重要で、整数の auto increment ではできないことです。
なぜ重複しないのか(内部構造が理由)
| 時刻(4byte) | マシン識別子(5byte) | プロセスID(2byte) | カウンタ(3byte) |
- マシン/プロセス単位で識別が入る
- さらにカウンタで連番が加わる
- 作成時刻の分まで情報が組み込まれている
→ 全マシン独立にIDを生成しても衝突しない仕組み
auto increment の危険な例
サーバーAとサーバーBが同時 INSERT
| サーバ | 生成ID | 競合? |
|---|---|---|
| A | 1001 | DBが発番するから順番待ち必要 |
| B | 1001 | 同じ数字を使おうとする |
DBがひとつならなんとかなるが、
- アプリケーションサーバーを増やす
- マイクロサービス間で非同期に生成したい
- オフラインで一旦生成→後で集約したい
こうなると整数IDは破綻しやすい。
ObjectId なら
server A → 6730b6a0d8a28f890b7c9f40
server B → 6730b6a0f4828c1112a0c954
- 発番の待ち合わせ無し
- 中央DB不要
- 重複リスク極小
- 完全に分散してIDを生成できる。
まとめ
| 方式 | 複数マシンで安全? | ID生成にDB必要? |
|---|---|---|
| auto increment (int) | 衝突しやすい | 必須 |
| UUID | ◎ 衝突ほぼ無し | 不要 |
| ObjectId | ◎ 衝突ほぼ無し+時系列で並ぶ | 不要 |
つまり ObjectId は「自律分散ID」+「時系列で見れるID」の両立が強みです。