TOC

NanoID 能不能取代 UUID

关于 UUID 可以浏览 UUID

最近听说一种新的 ID 生成器,叫做 NanoID,很多地方那个都把它拿来和 UUID 做对比。

根据查到的资料,好像最早是来自 JS 项目 NanoID。做比较的参考也是 NodeJS 版本的 UUID 实现。

这是两个 JS 库的 stars 数对比:NanoID starsUUID stars

相同之处:和 UUID4 差不多的随机位(UUID4 122,NanoID 126), 冲突概率差不多,而且都是 urlsafe 的。

更好的地方:

  1. 性能更好:资源占用少,计算速度快 (不一定适用于其他语言。但考虑到其设计的简单,性能更好应该比较正常)
  2. 采用更加大的字母表 => 更加紧凑
    0-9 a-z A-Z _ - 64 个字符,相当于 urlsafe base64
    相比之下,UUID 只采用 0-9 a-f 十六个字符
    所以最后,UUID 需要 36 个字符(包括四个横杠)来表示,NanoID 只需要 21 个字符。
  3. 采用更安全的硬件随机数生成器(NodeJS 版本采用 crypto 模块,浏览器环境采用 Web Crypto API,Python 版本采用 os.urandom
  4. 设计比 UUID 简单得多,代码量非常小,
    我看了 Python 版本的实现,只是生成指定字母表的随机字符串,如果不需要自定义字母表,其实现可以缩短到以下几行代码:

    from os import urandom
    alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-'
    def nanoid():
        id = ''
        while True:
            for random_byte in urandom(50):
                id += alphabet[random_byte & 63]
                if len(id) == 21:
                    return id
    

    PS: NanoID 的 Python 库真是有点过于累赘,明明一个很简单的逻辑,搞出好几个文件。

总结

UUID 的适用场景不会被这个简单的随机字符串替换,其复杂设计还是有它的价值在里面。

不过确实可以考虑采用更大的字母表来表示,将其缩短一些。

这个 NanoID 也没啥特别的,就是随机,这也算的话,我可以发表一种更简洁的算法:

from os import urandom
from base64 import urlsafe_b64encode

def angid():
    return urlsafe_b64encode(urandom(15)).decode()

def angid2():
    return urlsafe_b64encode(urandom(18)).decode()

andid 会生成 20 位长度的 ID,随机位 120,
angid2 会生成 24 位长度的 ID,随机位 144,
生成效率对比:

%timeit angid()
2.4 µs ± 34.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit angid2()
2.35 µs ± 20.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit nanoid() # 我上面的那个简化版本
6.75 µs ± 74.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit nanoid.generate()
10.1 µs ± 587 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit uuid.uuid1()
2.75 µs ± 171 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

%timeit uuid.uuid4()
3.57 µs ± 33.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

UUID 的生成速度确实令人震惊,比我想象中的要好得多。

PS: UUID 模块有部分采用 C 实现(_uuid)。
PS: Python UUID4(基于随机)采用了硬件随机数生成器 os.urandom