ソート可能な UUID を生成する ksuid を試してみる。

こんにちは k-jun です。今日は UUID のユニーク性を持ちながら、ソート可能な値を生成する ksuid を試してみます。

https://github.com/segmentio/ksuid

特徴としては以下の3つが挙げられており、Unix の sort コマンドでソートしてあげると生成順に並ぶようです。

  • Naturally ordered by generation time
  • Collision-free, coordination-free, dependency-free
  • Highly portable representations

よく似ているものとして ULID があるのでこちらとも以下の点を比較してみます。

  • データ型の取り回し
  • 生成速度

Logic

Binary KSUIDs are 20-bytes: a 32-bit unsigned integer UTC timestamp and a 128-bit randomly generated payload. The timestamp uses big-endian encoding, to support lexicographic sorting. The timestamp epoch is adjusted to May 13th, 2014, providing over 100 years of life. The payload is generated by a cryptographically-strong pseudorandom number generator.

32-bit 分の timestamp と 128-bit 分のランダムな payload で値を生成しているようです。先頭の箇所が timestamp であれば確かに生成順にソートは可能ですね。

UUID の生成方法も見てみると、128-bit で構成されるようなので KSUID は純粋に UUID の先頭に timestamp をつけた形式となりそうです。

ULID はどうでしょうか...?

README.md の図を参照すると、どうやら 48-bit 分の timestamp と、80-bit 分のランダムな payload で構成されるようです。

if the same millisecond is detected, the random component is incremented by 1 bit in the least significant bit position (with carrying). For example:

また、同じ millisecond に生成された ID に関しても順序を担保するべく、Random 関数が同じ milisecond に呼ばれたことを検知し、1 bit だけずらした ID を生成するようです。

同じ millisecond に生成された ID に関しての言及は自分が見た限りでは ksuid には見られないので、この点は ULID の方が信用できそうです。(記述あったらごめんなさい...) UUID v4 をそのままくっつけていそうなので、ここの順序性は気にしていないのかもしれません。

Install & Example

適当にインストールして使ってみます。

$ go install github.com/segmentio/ksuid/cmd/ksuid@latest
$ ksuid
1xJnDtfrgJucdg1AH3WJ3L1aHsV

同じ millisecond で ID を生成するのもなかなか難しそうです。

$ ksuid -n 3
1xJnHJ519QoMtt7v3Mo5XRfwcdn
1xJnHNjEekqR3EuhrrqVt6Ah4fj
1xJnHNUvkw0TBs7OnkmOpM4MMKC

inspect で timestamp を見ることもできるみたいです。

$ ksuid -f inspect 1xJnHJ519QoMtt7v3Mo5XRfwcdn


REPRESENTATION:

  String: 1xJnHJ519QoMtt7v3Mo5XRfwcdn
     Raw: 0DB6C5B13F4FD9C482CD769FFFA7D7D87F91A35B

COMPONENTS:

       Time: 2021-08-28 01:32:49 +0900 JST
  Timestamp: 230081969
    Payload: 3F4FD9C482CD769FFFA7D7D87F91A35B

Compirison

では、ULID と比較してみましょう。ULID の方も Install & Example

$ go get -v github.com/oklog/ulid/v2/cmd/ulid
$ ulid
01FE47GTB519KKW38PWC5TCQ6K

比較は普通に ID を cli で生成してみて、総実行時間を計測します。

ksuid

$ time bash -c 'for i in {1..10000}; do ksuid > /dev/null; done'
bash -c 'for i in {1..10000}; do ksuid > /dev/null; done'  13.98s user 12.55s system 86% cpu 30.714 total

ULID

$ time bash -c 'for i in {1..10000}; do ulid > /dev/null; done'
bash -c 'for i in {1..10000}; do ulid > /dev/null; done'  13.50s user 11.82s system 86% cpu 29.413 total

ほとんど差がない...。Performance はそこまで差分が無いんでしょうね... まあ乱数生成して timestamp くっつけるだけですしおすし。

データの取り回し

取り回しも ULID のほうが楽そうです。というのも、UUID と同じデータ長にしてくれているので、UUID 互換だからですね。

うーん、自分の比較ではどう考えても ULID のほうが便利そうです。必要になったら ULID を使いますかね...。 それでは今回はこのへんで。