UUIDの各バージョンとユースケース
UUIDはユニークなIDのデータ形式ですが、バージョンがいくつかあり、それぞれ用途が違います。
この記事ではそれぞれの用途について思いつく限り書いてみようと思います。
UUIDの形式
ざっくりUUIDの形式を説明すると、16進数(1~F)=4bitの文字がxxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
という感じで連なります。ハイフンを除いて、4bit*32文字=128bitです。
Mにはバージョン(1~5)が入ります。
Nにはバリアントが入ります。現行規格だと以下の4つのいずれかが入ります。
- 8(1000)
- 9(1001)
- A(1010)
- B(1011)
上記に示すように上位2ビットが(10)になります。
他のバリアントは、将来のために予約されていたり、マイクロソフトのGUIDなどとの互換性のために使われています。
例を出すと、18cd3748-9a76-4a05-8c69-ba0b8c1a9d17
のようなIDは、バージョンがv4、バリアントは標準規格のバリアントである8(1000)が採用されていることが分かります。
v1
v1はタイムスタンプベース(100ナノ秒単位)で、西暦5235年まで使えます。
ナノ秒は10億分の1秒なので、100ナノ秒は1000万分の1秒です。タイムスタンプ部だけでも被ればそこそこの奇跡です。
タイムスタンプベースですが、生成された時刻が一緒だからと言って必ずしも同じUUIDにはなりません。
形式は、
TTTTTTTT-TTTT-1TTT-sSSS-AAAAAAAAAAAA
となっており、
- T ・・・ タイムスタンプ
- S ・・・ クロックシーケンス(小文字のsは前述のバリアントです)
- A ・・・ MACアドレス
となっています。
つまり、タイムスタンプが一緒でも、クロックシーケンスが違ったり、MACアドレスが違う(つまり機器が違う)なら一意になります。
クロックシーケンスは、タイムスタンプとMACアドレスが被った場合にインクリメントすることで重複を避けるための余地のようなものです。バリアントが4種と3*4=12bitで合計14bitあるので、10進数で16384までインクリメントできます。
肝心のユースケースとしては、一つのサーバーやIoT機器、端末内で100ナノ秒以下でクロックシーケンスの許容量(16384)を超える頻度でIDを生成することが無ければ問題がありません。
しかも、シーケンシャルであるという強みもあるので、テーブルのプライマリキーとしても優秀です。(もっとも最近はULIDやCLIDがありますが)→ 参考
つまり、ほとんどのケースはv1でいいはずですが、タイムスタンプは機器の時刻設定に左右されるため、時刻を戻したりすると重複の可能性が出てきます。
単純にランダムなだけでいいなら、v4の方が使い勝手が良いでしょう。
v2
v2は認可のための情報を含めることができます。具体的には、POSIXのユーザーIDあるいはグループIDです。([詳細](https://ja.wikipedia.org/wiki/ユーザー識別子)
あまりユースケースが思い浮かばないのですが、社内システム関連のアプリケーションを作っている人などは頭に入れておくとどこかで使うかもしれません。
管理された端末やサーバーを認証および認可するためのアクセストークンとしては使えるかも?という所感です。
v3/v5
v3とv5はハッシュアルゴリズムが違うだけで、どちらも一意な情報から対応するUUIDを生成したいときに利用できます。
要はUUIDへのハッシュ用途です。
これらのユースケースを考えるときは、
これはv4の用途を考える際にも重要なのですが、システム上のデータには
- アプリの設定として事前に用意しておく必要がある静的なデータ
- アプリをリリース後に自由に登録されるような動的なデータ
があると思っています。
v3/v5は前者のような静的なデータのIDとして最適です。(逆にv1およびv4は後者に最適と言えます)
例えば、カレンダーのマスタデータのようなデータは、日付で一意になるIDがidカラムに入っていると都合が良いため、v3あるいはv5が最適です。
ではv3とv5のどちらを利用すべきかというと、パフォーマンス重視の場合はv3で良いと思います。
v3はMD5、v5はSHA-1のハッシュアルゴリズムを使いますが、一般的にMD5の方がパフォーマンスが良いです。ただし、MD5は暗号用途に使うには強度が低いため、IDから元の値を複合されるとまずい場合は使うべきではありません。
ただし、単純なハッシュ化による一意なIDの生成用途であれば問題は無いです。
SHA-1はMD5よりセキュアですが昨今は脆弱性を指摘されていて、2030年12月31日には仕様が廃止されます。利用をやめているシステムも多いです。暗号化用途には最新のSHA-3や量子耐性のある暗号アルゴリズムを使うべきで、その点v5はあまり利用する価値がありません。
よって、一意な情報を元に一意なUUIDを生成したい場合は、v3を使うと良いでしょう。
v4
最後に、おそらく一番使われているのではないかと思われるv4ですが、こちらはランダムなUUIDを生成したい場合に利用されます。
例えば、ユーザーが新規登録したデータなどには最適でしょう。
v4には2種類のバリアントがあり、バリアント1は122bitが使えるので5.31潤、バリアント2は121bitを使えるためその半分のエントロピーを持ちます。
乱数(あるいは疑似乱数)生成のアルゴリズム自体は指定はありませんが、十分に衝突確率の低いモノが採用されます。
終わりに
かなりいまさらな記事になった感はありますが、UUIDには使うと決めてしまえばあとはバージョンを選ぶだけの気軽さがあります。
もちろん細かく選定するならば、
- ランダムでシーケンシャルなID → ULIDやCLIDのパッケージをライブラリで検索する。またはUUIDv6~v8を使う。
- ハッシュ用途 → ライブラリで"hash"で検索し、ポピュラーでパフォーマンスに優れたパッケージを選定する
ということはできるので、極限までパフォーマンスに拘りたいならUUIDは一般化されすぎていると思いますが、システム間の互換性など標準仕様が存在する形式を採用するメリットもあります。
一般的なシステムであれば、ID生成がパフォーマンス上のボトルネックになるようなことは極めて少ないと思うので、UUIDは殆どの場合において最適な選択肢かと思います。
この記事が読んでいただいた方の参考になれば幸いです。
Discussion