UUID衝突は起きる【誤った運用パターン】
UUIDを主キーにしていたのに、ある日duplicate keyが出る。この瞬間「UUIDって結局ぶつかるのでは」という話になりがちです。
しかし実務で起きるUUID重複の多くは、UUIDという規格そのものの問題ではなく、規格が前提にしている生成条件を実装や運用で壊しているケースです。
1. まず結論:危ないパターン一覧
| パターン | 何が起きるか | まずやるべき対策 |
|---|---|---|
| 固定seedや弱いPRNGでUUIDv4を自作 | 別プロセス・別ノードで同じ系列が再現 | OS/ランタイム標準のUUID APIを使う |
| fork、VM snapshot、コンテナ複製後に生成状態を引き継ぐ | 乱数やカウンタの状態が巻き戻り重複が出る | fork後の再seed、clone後の再初期化 |
| UUIDv3/v5を「毎回新しいID」と誤解 | 同じ入力から同じUUIDが再生成される | 決定論的IDと理解し用途を限定する |
| UUIDv1/v6/v7/v8を自前実装 | 高頻度生成や複数ノードで重複しやすくなる | 既存ライブラリを使い独自生成器を減らす |
| UUIDを途中で切り詰める | 元の128ビットの一意性を自分で捨てる | 保存・比較はフル長で行う |
| DB側にUNIQUE/PRIMARY KEYを置かない | 重複が静かに混入し原因調査が遅れる | ストレージ層で一意制約を持つ |
2. UUIDのバージョンごとの性質
| バージョン | 方式 | 注意点 |
|---|---|---|
| UUIDv4 | ランダムベース(122ビット乱数) | CSPRNGを使うべき |
| UUIDv7 | タイムスタンプ+乱数/カウンタ | ソートしやすい、高頻度生成時のカウンタ設計注意 |
| UUIDv3/v5 | name-based(決定論的) | 同じ入力→同じUUID。採番用途には使わない |
| UUIDv8 | 実験用・ベンダー独自 | 一意性は実装依存。前提にしてはいけない |
3. パターン1:弱いPRNGでUUIDv4を自作する
Math.random()相当の一般用途PRNGで128ビット分作り、起動時にtime()やPIDでseedを入れる。見た目はUUIDでも、乱数源が弱ければ同じ系列が別プロセスで再現されます。
RFC 9562はUUIDの一意性と予測困難性のためにCSPRNGを使うべきとしています。Pythonのuuid.uuid4()も暗号学的に安全な方法で生成します。
対策: UUIDを自作しない。乱数seedを手でいじらない。標準ライブラリをそのまま使う。
4. パターン2:fork、snapshot、cloneで生成状態を巻き戻す
- VM snapshot取得後に同じイメージを複数復元する
- コンテナイメージ起動時に同じ初期状態から独自生成器が立ち上がる
- worker fork後にPRNG状態やカウンタ状態を共有してしまう
こうした運用ではUUIDの生成系列が意図せず再現されえます。
対策:
- 独自のUUID生成状態を長く持たない
- fork/clone/restoreの直後に再初期化する
- 可能ならOS由来の乱数を毎回利用する実装に寄せる
5. パターン3:UUIDv3/v5を毎回新しいIDと誤解する
UUIDv3/v5は「重複しない採番」ではなく「同じ入力なら同じID」です。RFC 9562でも、同じnamespace+同じnameから生成したUUIDは等しくなければならないと書かれています。
間違った使い方の例:
uuid5(NAMESPACE_URL, "https://example.com/users/42")を毎回「新規採番」として使う- tenantをnamespaceに入れず、全顧客共通namespace+emailで発番する
対策: UUIDv3/v5は決定論的IDと理解し用途を限定する。namespace設計を曖昧にしない。
6. パターン4:時刻系UUIDを自前実装する
UUIDv1/v6/v7/v8は見た目だけ真似すると危ないです。
- v1/v6: MACアドレスだから一意だろうと決め打ちしない。RFCも「仮想マシンやコンテナの登場によりMACアドレスの一意性は保証されない」としています。
- v7: 同一ミリ秒内で大量発番するのにカウンタ設計がない、時刻が戻ったときに何もせず生成を続ける、といった実装は危険。
- v8: 「timestamp+shard id+適当にrandom」という自社独自UUIDは、その設計書がUUIDの一意性仕様そのものです。レビューなしで入れるのは危険。
7. パターン5:UUIDを途中で短くしてしまう
- 先頭8文字だけを外部キー代わりに使う
- 128ビットUUIDを64ビット整数に潰す
- 文字列カラム長が足りず末尾が切れる
- ログや画面表示の短縮表現をそのまま一意キー扱いする
表現を変えること自体は悪くありません。ハイフンを外す、小文字/大文字をそろえる、バイナリ16バイトで持つ、など128ビットを落とさない変換は問題ありません。危ないのは一意性の材料そのものを削る変換です。
8. パターン6:DB側に一意制約がない
UUIDが十分衝突しにくいとしても、本当に重複を許容できないなら、保存先でも一意制約を持つべきです。RFC 9562も、UUIDは実装上十分な一意性を提供できる一方で、真のglobal uniquenessを絶対保証することはできないとしています。
実務での基本:
- UUIDは衝突しにくいIDとして使う
- DBはUNIQUE/PRIMARY KEYで最終防衛線を持つ
- 重複時のretry/idempotency/incident loggingを設計する
9. 実務向けチェックリスト
- UUIDを自前生成していないか確認する — 標準APIに寄せられるなら寄せる
- UUIDのversionを仕様として決める — v4/v7はランダム系、v3/v5は決定論的、v8は独自仕様
- seedとgenerator stateの扱いを棚卸しする — fork、snapshot、clone後に同じ状態を引き継がない
- 保存時にフル長を維持しているか確認する — 短縮表示を本来キーとして使わない
- DBにUNIQUE/PRIMARY KEYを置く — UUIDは確率を下げる仕組みであり、制約そのものではない
- 重複を観測できるようにする — duplicate keyを握りつぶさず追跡可能にする
まとめ
UUIDの衝突事故は、たいていUUIDが弱いのではなく、UUIDの前提を実装や運用で壊しているところから始まります。重複を見つけたら、まず疑うべきはUUIDの数学より、生成器、状態管理、保存形式、制約設計です。
関連する記事
同じタグを共有する最新の記事です。さらに近い話題で知識を深められます。
Windowsはなぜ今の形になったのか:開発者から見た歴代Windowsの進化
Windows 95からWindows 11までの変化を、見た目の年表ではなく、互換性、安定性、権限管理、ドライバ、Win32、.NET、セキュリティなどWindowsアプリ開発者の視点で整理します。
Windowsアプリ開発者のためのCPU設定入門:優先度・アフィニティ・Pコア/Eコア
Windowsアプリ開発者向けに、CPU優先度、アフィニティ、Pコア/Eコア、省電力設定、EcoQoS/Efficiency Modeの関係と、性能・応答性・発熱を測る考え方を整理します。
開発者の異常な愛情、または私は如何にして心配するのをやめてWindowsを愛するようになったか
Windowsは面倒くさい。けれど、その面倒くささは、現実の業務を背負ってきたOSだからこその面倒くささでもある。
PowerShell実用コマンド集 ── 日常作業でよく使う小さな機能を増やす
PowerShellで日常作業に使う実用コマンドとして、Measure-Object、Group-Object、Select-String、Compare-Object、Tee-Object、Start-Transcriptなどの使いどころを整理します。
PowerShellスクリプト応用 ── ログ調査・アーカイブ・レポート化を安全に自動化する
PowerShellスクリプトでログ調査、CSVレポート、古いログのアーカイブ、証跡保存、タスクスケジューラ実行までを安全に進める実務手順を整理します。
関連トピック
このテーマと近いトピックページです。記事を起点に、関連するサービスや他の記事へ進めます。
Windows技術トピック
Windows 開発、不具合調査、既存資産活用の技術トピックをまとめた入口です。