ID を採番するときによく使われる UUID Version 4 の課題として「順序性がなくソートしにくい」という側面があり,ULID (Universally Unique Lexicographically Sortable Identifier) を使えばソートできるようになるという記事を前に書いた.
関連して調査をしていたら,標準化団体 IETF (Internet Engineering Task Force) によって,UUID Version 6, 7, 8 という新しい仕様が提案(ドラフト段階)されていることを発見した❗️UUID Version 6, 7, 8 の目的を簡単にまとめると「タイムスタンプ情報を使ってソートできる ID を採番できるようにする」となり,もしこの仕様が取り込まれると,UUID を活用する幅がさらに広がりそう.
- UUID Version v6: グレゴリオ暦ベース(UUID Version v1 の改善)
- UUID Version v7: Unix Time Stamp ベース
- UUID Version v8: 独自仕様(実験的もしくはベンダー固有の要件で使う)
詳細な仕様は以下に載っている.記事を書いている2022年10月時点ではまだドラフト段階なので,変わる可能性はある!
uuid6/prototypes リポジトリ
GitHub の uuid6/prototypes リポジトリを見ると,いろいろな言語で実装された UUID Version 6, 7, 8 用のライブラリが載っている.
今回は UUID Version v6 と UUID Version v7 を試すため,Pyhon で最新ドラフト (04) までをサポートしている uuid6 を使う.pip で簡単にセットアップできる.なお,名前は uuid6 だけど,実装としては UUID Version v6 と UUID Version v7 どちらも試せる.
UUID Version 6 (UUID v6)
ドラフト仕様を読むと,UUID v6 は UUID v1 を改善するために策定された仕様で,UUID v1 のフィールドの構成を見直したものになる.UUID v1 は「タイムスタンプ情報」を持つけど,最下位部分と上位部分が逆転していて,ソートはできない.UUID v6 はあくまで UUID v1 の課題を解決したものとなり,基本的には UUID v7 を使うとドラフト仕様に書いてある.
UUID v6 のタイムスタンプ情報は UUID v1 と同じく「グレゴリオ暦ベース (60 bit)」で,1582年10月15日からの 100 ns 単位のカウントとなる.記事を書きながら採番した UUID v6 の例を以下に載せておく.
- 1ed575d3-b55c-6f9c-93a7-e81a4645882c
- 1ed575d3-c876-609c-9270-7532b69c8a8e
- 1ed575d3-db8a-669c-8634-8a0ba964ffa3
UUID Version 1 is a time-based UUID featuring a 60-bit timestamp represented by Coordinated Universal Time (UTC) as a count of 100- nanosecond intervals since 00:00:00.00, 15 October 1582 (the date of Gregorian reform to the Christian calendar).
UUID v6 のフィールド構成は 128 bit 長で以下の通り.より詳細な表現はドラフト仕様を参照で!なお,ID の中に「バージョン情報」も含まれるため,3 ブロック目の1文字目は必ず 6
になる.
time_high
: タイムスタンプ情報の最上位 32 bittime_mid
: タイムスタンプ情報の中間 16 bittime_low_and_version
: バージョン情報 4 bit とタイムスタンプ情報の最下位 12 bitclk_seq_hi_res
: UUID variant10
2 bit とクロックシーケンス情報の上位 6 bit などclk_seq_low
: クロックシーケンス情報の下位 8 bitnode
: 一意の識別子 48 bit
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_high | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | time_mid | time_low_and_version | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |clk_seq_hi_res | clk_seq_low | node (0-1) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | node (2-5) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
UUID v6 を Python で試す
uuid6
ライブラリを使うと簡単に UUID v6 で採番できる.以下では2秒間隔で「3種類」の id を採番して,期待通りに比較できている.比較できるということはソートもできる!
from uuid6 import uuid6 import time # 1ed575d3-b55c-6f9c-93a7-e81a4645882c id1 = uuid6() print(id1) time.sleep(2) # 1ed575d3-c876-609c-9270-7532b69c8a8e id2 = uuid6() print(id2) time.sleep(2) # 1ed575d3-db8a-669c-8634-8a0ba964ffa3 id3 = uuid6() print(id3) # 比較 print(id1 < id2) # True print(id2 < id3) # True print(id3 < id1) # False
UUID Version 7 (UUID v7)
ドラフト仕様を読むと,UUID v7 はよく使われる Unix Time (32 bit) にミリ秒を追加した 48 bit を使う. 記事を書きながら採番した UUID v7 の例を以下に載せておく.
- 018422b2-4843-7a62-935b-b4e65649de3e
- 018422b2-5013-7f02-830f-2563ce4533df
- 018422b2-57e8-79b2-8b59-d6926217a9dc
UUID v7 のフィールド構成も 128 bit 長で以下の通り.より詳細な表現はドラフト仕様を参照で!なお,ID の中に「バージョン情報」も含まれるため,3 ブロック目の1文字目は必ず 7
になる.
unix_ts_ms
: タイムスタンプ情報 48 bitver
: バージョン情報 4 bitrand_a
: 乱数 12 bitvar
: variant 2 bitrand_b
: 乱数 62 bit
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | unix_ts_ms | ver | rand_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | rand_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
UUID v7 を Python で試す
uuid6
ライブラリを使うと簡単に UUID v7 で採番できる.以下では2秒間隔で「3種類」の id を採番して,期待通りに比較できている.比較できるということはソートもできる!
from uuid6 import uuid7 import time # 018422b2-4843-7a62-935b-b4e65649de3e id1 = uuid7() print(id1) time.sleep(2) # 018422b2-5013-7f02-830f-2563ce4533df id2 = uuid7() print(id2) time.sleep(2) # 018422b2-57e8-79b2-8b59-d6926217a9dc id3 = uuid7() print(id3) print(id1 < id2) # True print(id2 < id3) # True print(id3 < id1) # False
UUID Version 8 (UUID v8)
ドラフト仕様を読むと,UUID v8 は限定的な用途のために策定されていて,正確には「実験的」もしくは「ベンダー固有」と書いてある.UUID v8 のフィールド構成も 128 bit 長だけど,UUID v7 と同じく ver
と var
は必須で,他は自由に設定できる.1点注意点としては,ドラフト仕様に「ランダムで埋めちゃダメ」と書いてあるので,あくまで独自仕様のタイムスタンプ情報を含めることが前提になりそう.例えば「何かしらの理由でタイムスタンプを知られたくない」というときに使えそう?
To be clear: UUIDv8 is not a replacement for UUIDv4 where all 122 extra bits are filled with random data.
custom_a
: タイムスタンプ情報 48 bitver
: バージョン情報 4 bitcustom_b
: 乱数 12 bitvar
: variant 2 bitcustom_c
: 乱数 62 bit
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | custom_a | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | custom_a | ver | custom_b | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |var| custom_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | custom_c | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+