Universally Unique Identifier(UUID)は、ほとんどの実用的な目的で安全に一意であると見なすことができる特定の形式の識別子です。 正しく生成された2つのUUIDは、別々の関係者によって2つの異なる環境で作成された場合でも、同一になる可能性はほとんどありません。 これが、UUIDが 普遍的に 個性的。
この記事では、UUIDの特性、その一意性がどのように機能するか、およびUUIDがリソースの識別を簡素化できるシナリオについて説明します。 データベースレコードと相互作用するソフトウェアの一般的な観点からUUIDにアプローチしますが、分散型の一意のID生成が必要なすべてのユースケースに広く適用できます。
実際にはUUIDとは何ですか?
UUIDは、単に一意として安全に扱うことができる値です。 衝突のリスクは非常に低いため、それを完全に無視することを合理的に選択できます。 異なる用語(GUID、またはグローバル一意識別子がMicrosoftの推奨セマンティクス)を使用して参照されているUUIDが表示される場合がありますが、意味と効果は同じです。
真のUUIDは、標準化された形式で生成および表現される一意の識別子です。 有効なUUIDはRFC4122で定義されています。 この仕様では、中央の発行機関がなくても、実装全体で一意性を維持するUUIDを生成するために使用できるアルゴリズムについて説明します。
RFCには、それぞれが異なるメカニズムを使用して値を生成する5つの異なるアルゴリズムが含まれています。 利用可能な「バージョン」の概要は次のとおりです。
- バージョン1-時間ベース –タイムスタンプ、クロックシーケンス、および生成デバイスに固有の値(通常はそのMACアドレス)を組み合わせて、その時点でそのホストに固有の出力を生成します。
- バージョン2–DCEセキュリティ –このバージョンは、分散コンピューティング環境(DCE)で使用するためのバージョン1の進化形として開発されました。 広く使われていません。
- バージョン3–名前ベース(MD5) – MD5は、「名前空間」と「名前」をハッシュして、名前空間内でその名前に固有の値を作成します。 同じ名前空間と名前で別のUUIDを生成すると同じ出力が生成されるため、このメソッドは再現性のある結果を提供します。
- バージョン4–ランダム –最近のほとんどのシステムは、ホストの乱数のソースを使用して値を発行するため、UUIDv4を選択する傾向があります。 同じUUIDが2回生成される可能性は、事実上ごくわずかです。
- バージョン5–名前ベース(SHA-1) –これはバージョン3に似ていますが、より強力なSHA-1アルゴリズムを使用して入力名前空間と名前をハッシュします。
RFCはアルゴリズムをバージョンと呼んでいますが、バージョン5は一見最新のように見えるため、常にバージョン5を使用する必要があるという意味ではありません。 どちらを選択するかは、ユースケースによって異なります。 多くのシナリオでは、ランダムな性質のためにv4が選択されます。 これにより、単純な「新しい識別子を教えてください」シナリオの理想的な候補になります。
生成アルゴリズムは、128ビットの符号なし整数を出力します。 ただし、UUIDは、より一般的には16進文字列と見なされ、16文字のバイナリシーケンスとして格納することもできます。 UUID文字列の例を次に示します。
16763be4-6022-406e-a950-fcd5018633ca
値は、ダッシュ文字で区切られた英数字の5つのグループとして表されます。 ダッシュは文字列の必須コンポーネントではありません。 それらの存在は、UUID仕様の歴史的な詳細にかかっています。 それらはまた、人間の目が識別子をはるかに認識しやすくします。
UUIDのユースケース
UUIDの主な使用例は、一意の識別子の分散型生成です。 UUIDはどこからでも生成でき、バックエンドコード、クライアントデバイス、データベースエンジンのいずれからのものであっても、一意であると安全に見なすことができます。
UUIDは、切断された環境全体でのオブジェクトIDの決定と維持を簡素化します。 歴史的に、ほとんどのアプリケーションは、主キーとして自動インクリメント整数フィールドを使用していました。 新しいオブジェクトを作成しているときは、そのIDを知るまではわかりませんでした 後 データベースに挿入されていました。 UUIDを使用すると、アプリケーションのかなり早い段階でIDを判別できます。
これが違いを示す基本的なPHPデモです。 最初に整数ベースのシステムを見てみましょう。
class BlogPost { public function __construct( public readonly ?int $Id, public readonly string $Headline, public readonly ?AuthorCollection $Authors=null) {} } #[POST("/posts")] function createBlogPost(HttpRequest $Request) : void { $headline = $Request -> getField("Headline"); $blogPost = new BlogPost(null, $headline); }
初期化する必要があります $Id
とのプロパティ null
実際のIDがわからないので 後 データベースに永続化されています。 これは理想的ではありません– $Id
本当にnull許容であってはならず、それは可能です BlogPost
不完全な状態で存在するインスタンス。
UUIDに変更すると、問題が解決します。
class BlogPost { public function __construct( public readonly string $Uuid, public readonly string $Headline, public readonly ?AuthorCollection $Authors=null) {} } #[POST("/posts")] function createBlogPost(HttpRequest $Request) : void { $headline = $Request -> getField("Headline"); $blogPost = new BlogPost("16763be4-...", $headline); }
重複する値のリスクを冒すことなく、アプリケーション内で投稿IDを生成できるようになりました。 これにより、オブジェクトインスタンスは常に有効な状態を表し、不格好なnull許容IDプロパティは必要ありません。 このモデルにより、トランザクションロジックの処理も容易になります。 親への参照が必要な子レコード(私たちの投稿など Author
アソシエーション)は、親が割り当てられたIDをフェッチするためのデータベースラウンドトリップなしで、すぐに挿入できます。
将来的には、ブログアプリケーションがより多くのロジックをクライアントに移動する可能性があります。 おそらく、フロントエンドは完全なオフラインドラフト作成のサポートを取得し、効果的に作成します BlogPost
ユーザーのデバイスに一時的に永続化されるインスタンス。 これで、クライアントは投稿のUUIDを生成し、ネットワーク接続が回復したときにそれをサーバーに送信できます。 その後、クライアントがサーバーのドラフトのコピーを取得した場合、UUIDはすでにわかっているため、残りのローカル状態と一致させることができます。
UUIDは、さまざまなソースからのデータを組み合わせるのにも役立ちます。 整数キーを使用するデータベーステーブルとキャッシュのマージは、面倒でエラーが発生しやすい場合があります。 UUIDは、テーブル内だけでなく、ユニバース全体のレベルで一意性を提供します。 これにより、異なるストレージシステム間で頻繁に移動される複製された構造やデータの候補としてはるかに適しています。
UUIDがデータベースと出会うときの警告
UUIDの利点は非常に魅力的です。 ただし、実際のシステムで使用する場合は、注意すべき点がいくつかあります。 整数IDを支持する大きな要因の1つは、スケーリングと最適化が簡単なことです。 データベースエンジンは、一方向にのみ進む数値のリストを簡単にインデックス付け、並べ替え、およびフィルタリングできます。
UUIDについても同じことは言えません。 まず、UUIDは整数の4倍の大きさです(36バイト対4バイト)。 大規模なデータセットの場合、これ自体が重要な考慮事項になる可能性があります。 また、特に最も一般的なランダムUUIDの場合、値の並べ替えとインデックス作成は非常に困難です。 それらのランダムな性質は、それらが自然な秩序を持たないことを意味します。 これは、UUIDを主キーとして使用する場合、インデックス作成のパフォーマンスを低下させます。
これらの問題は、外部キーを多用する十分に正規化されたデータベースで悪化する可能性があります。 これで、多くのリレーショナルテーブルが作成され、それぞれに36バイトのUUIDへの参照が含まれる場合があります。 最終的に、結合と並べ替えを実行するために必要な追加のメモリは、システムのパフォーマンスに大きな影響を与える可能性があります。
UUIDをバイナリデータとして保存することで、問題を部分的に軽減できます。 つまり、 BINARY(16)
代わりに列 VARCHAR(36)
。 PostgreSQLなどの一部のデータベースには組み込みが含まれています UUID
データ・タイプ; MySQLのような他のものには、UUID文字列をそのバイナリ表現に(またはその逆に)変換できる関数があります。 このアプローチはより効率的ですが、データの保存と選択に追加のリソースを使用することを忘れないでください。
効果的な戦略は、主キーとして整数を保持し、アプリケーションの参照用にUUIDフィールドを追加することです。 リレーショナルリンクテーブルは、IDを使用して、コードがUUIDを使用してトップレベルオブジェクトをフェッチおよび挿入する際のパフォーマンスを向上させることができます。 すべては、システム、その規模、および優先順位に依存します。分散ID生成と単純なデータマージが必要な場合、UUIDが最良のオプションですが、トレードオフを認識する必要があります。
概要
UUIDは、分散IDの生成に安全に使用できる一意の値です。 衝突 それは 可能ですが、非常にまれであるため、検討対象から除外できます。 1世紀全体で1秒間に10億のUUIDを生成した場合、十分なエントロピーが利用可能であると仮定すると、重複に遭遇する確率は約50%になります。
挿入が発生する前に、UUIDを使用してデータベースとは独立してIDを確立できます。 これにより、アプリケーションレベルのコードが簡素化され、不適切に識別されたオブジェクトがシステムに存在するのを防ぎます。 UUIDは、テーブルレベルで動作する従来の整数キーとは異なり、データストア、デバイス、または環境に関係なく一意性を保証することにより、データレプリケーションも支援します。
UUIDは現在、ソフトウェア開発に広く普及していますが、完全なソリューションではありません。 新規参入者は衝突の可能性に固執する傾向がありますが、システムが非常に敏感で一意性を保証する必要がある場合を除いて、これを最優先事項にすべきではありません。
ほとんどの開発者にとってより明白な課題は、生成されたUUIDの保存と取得に関するものです。 素朴に VARCHAR(36)
(またはハイフンを削除して使用する VARCHAR(32)
)ほとんどのデータベースインデックスの最適化は効果がないため、時間の経過とともにアプリケーションが機能しなくなる可能性があります。 データベースシステムに組み込まれているUUID処理機能を調べて、ソリューションから可能な限り最高のパフォーマンスが得られるようにします。