UUID 以外のユニークなIDって何があるの？


# はじめに

いつもユニークな ID に UUID を使っているのですが、他には何かあるのでしょうか？
気になったので調べてみました！

# 紹介するもの

この記事ではこれらを紹介します

- UUID / [npm](https://www.npmjs.com/package/uuid) [github](https://github.com/uuidjs/uuid)
- ULID / [npm](https://www.npmjs.com/package/ulid) [github](https://github.com/ulid/spec)
- CUID / [npm](https://www.npmjs.com/package/cuid) [github](https://github.com/paralleldrive/cuid)
- NanoID / [npm](https://www.npmjs.com/package/nanoid) [github](https://github.com/ai/nanoid)

# 利用数

npm のDL数を2022年の月別で比べてみます。グラフは [npm-stat](https://npm-stat.com/charts.html) を使わせていただきました。

UUID と NanoID がよく使われているようです。
UUID が NanoID の 3倍、NanoID が CUID の30倍使われています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748702/41ea51e6-7aca-eb21-d85b-c7211e39a268.png)

UUID　と NanoID を除くと以下のようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748702/2fbbc2d3-8779-8c1b-250c-6bfb159189e6.png)

# それぞれの特徴

UUID がとても使われているのはわかりました。続いて、それぞれの特徴を紹介します。

## UUID

まずはみんな大好き UUIDです！ここではメジャーなバージョン4を紹介します。

UUIDバージョン4 は16進数の文字32個で構成されています。ひとつのUUIDにつき128ビットありますが、そのうち6ビット (1文字と半分) を規格で使っています。

形式は以下です。
`RRRRRRRR-RRRR-4RRR-rRRR-RRRRRRRRRRRR` ※Rがランダム、4が固定、rが2ビットだけ固定

6ビットを使っているので、122ビットが乱数になりますね。つまり $2^{122} \approx 5.3×10^{36}$  通りの UUID が存在することになります。

:::note info
衝突確率について、衝突するまでの生成回数の期待値は $2^{122/2} \approx 2.3*10^{18}$ 個となります。まず衝突しないですね。
参考：[誕生日攻撃](https://ja.wikipedia.org/wiki/%E8%AA%95%E7%94%9F%E6%97%A5%E6%94%BB%E6%92%83)
:::


# NanoID

次は NanoID です！

NanoID はUUIDの問題とされていた、「生成結果の文字列が長い」ことを解決したものです。
UUID が36文字なのに対し、NanoID は21文字です。かなり短くなりましたね。

また、ランダム部分が 126ビットあって UUID の122ビットと比べて少し強固です。

形式です。
`V1StGXR8_Z5jdHi6B-myT`

生成速度が気になっていたのですが、NanoID の作者がまとめてくださっていました。ありがたい。

```sh
$ node ./test/benchmark.js
crypto.randomUUID         21,119,429 ops/sec
uuid v4                   20,368,447 ops/sec
@napi-rs/uuid             11,493,890 ops/sec
uid/secure                 8,409,962 ops/sec
@lukeed/uuid               6,871,405 ops/sec
nanoid                     5,652,148 ops/sec
customAlphabet             3,565,656 ops/sec
secure-random-string         394,201 ops/sec
uid-safe.sync                393,176 ops/sec
cuid                         208,131 ops/sec
shortid                       49,916 ops/sec
```

# ULID

続いてULIDです！

ULID は、UUIDの問題を解消したものとなっています。
ULID が解消した UUIDの問題は 「生成結果の文字列が長い」「ソートできない」 です。
そう。ULIDは生成時間でソートできるのです。よさそうですね。

形式は以下です。
`01AN4Z07BY79KA1307SR9X4MV3` ※前半48bit がtimestamp で、後半80bit がランダムです。

前半に timestamp があるからソートできるんですね！また、16進数ではなく英数字を使うことで UUID よりも文字数が減っています。
タイムスタンプは西暦 10889年まで対応しています。未来永劫つかえる UUID に比べると短い期間ですが、それでも十分でしょう。
UUID に比べてランダム部分のbit数が少ないですが、同じミリ秒内に $1.21*10^{24}$ 個までの生成であればソート可能性が保たれるとのことです。timestamp がミリ秒単位で変わるので、こちらも実用上は問題なさそうですね。

# CUID

最後にCUIDです！

CUID は UUID の問題を解決しながら、ULID よりも良いとされているものです。
ULID との具体的な違いはこちら https://github.com/paralleldrive/cuid/issues/103

まずは形式です。
`ch72gsb320000udocl363eofy`
分解するとこうなります。 `c - h72gsb32 - 0000 - udoc - l363eofy`
`c`: 固定値
`h72gsb32`: timestamp
`0000`: Counter 
`udoc` : クライアントのfingerprint
`l363eofy`: ランダム

です。UUID や ULIDに比べていろいろありますね。

CUID の特徴です。

- スケーラブル
- fingerprintがあるので、複数のクライアント間では衝突しない
- 水平方向のスケーラビリティができる
- パフォーマンスが良い

ULID に比べてランダム部が小さいですが、同じミリ秒内に10000までの生成であればソート可能性が保たれます。同じくtimestamp がミリ秒で変わるので、実用上は問題ないことがほとんどだと思います。

:::note warn
ソースを見てみると、非推奨となっている `String.prototype.substr()` が使われていました。要注意かも？
:::

# さいごに

私は UUID しか知らなかったのですが、生成順でソートできるのは良さそうですね！
それぞれの利点と欠点を知って、使いわけるのはかなりアリだなって思いました。