- 原文地址:Introducing a URL-Friendly Unique Identifier — NanoID
- 原文作者:Apoorv Tyagi
- 译文出自:掘金翻译计划

引言
在每个软件系统中,我们都需要唯一的 IDs 来区分不同的对象。 最近,我写了一篇关于如何在大规模分布式环境下产生唯一 ID 的博文。在文章中,我们稍微了解了 UUIDs。
而在本文,我将分享一种 UUIDs 的替代方案——NaoID,它不仅能帮助你适应 uuids 所满足的场景,还能克服 uuids 中所存在缺点。
NaoID 介绍
NaoID 是一个轻量、安全、URL-友好型的唯一字符串 ID 生成器。
-
轻量。 相比于 UUID,NaoID 的体积更小。这让 NaoID 在信息传送和存储时更加游刃有余,如果放在大规模的系统下,效果将更加显著。
-
安全。NaoID 使用一种强加密的 API,因此比 Math.Random() 更加安全。这些 API 模块使用不可预测的硬件随机数生成器(或者真随机数生成器zh.wikipedia.org/wiki/%E7%A1…
-
可靠。NaoID 中,使用了自己的
统一算法(link.zhihu.com/?target=htt… % 生成(会导致符号分布不均匀),这是在开发 ID 生成器时经常存在的错误。(在某些情况下甚至会传播)。 -
更大的字母表。因此,NaoID 产生的 id 长度会更短(相比 uuid )。
NaoID 另外一个强大的地方在于,它允许使用者自定义字母表,你可以随意的更改字面意思或者要展示 id 的维度,例如,指定自定义的字母设置为 ‘1234567890ABCDEF’ ,id 的维度设置为 10,那么有:
import { alphabet } from 'nanoid';
const nanoid = alphabet ('1234567890ABCDEF', 10);
model.id = nanoid();
-
不依赖三方包。由于NanoID不依赖于任何第三方的依赖,随着时间的推移,它将有利于更加稳定的自我管理(不依赖于其他包,所以会比较稳定)。 从长远来看,这也非常有利于优化包的大小,并使其不容易出现由于依赖性所带来的问题。
-
- NanoID 支持多语言, 包括 — C#, C++, Dart & Flutter, Go, Java, PHP, Python, Ruby, Rust, Swift, 等等.
性能参考
使用示例
生成 NanoID 或 UUID 非常简单。在 JavaScript 中,可以通过 npm 包引入使用,传送门 => github.com/ai/nanoid. 主模块使用 URL 友好性的符号(A-Za-z0–9_-),并且返回长度为 21 字符的 ID。
import { nanoid } from "nanoid";
model.id = nanoid() // X2JaSYP7_Q2leGI9b-MyA
你也可以指定 ID 的长度
nanoid(9); // "wMeKBp6th"
当然你也可以额使用自己的字母表生成 ID
const alphabet = '0123456789ABCDEF';
generate(alphabet, 9); // F65BF3050
风险
尽管 NaoID 使用其自身的默认字母表,每秒能产生超过 2.2 百万的唯一 IDs,虽然仍有可能产生相同的多个 IDS,但是通过计算,理论上发生相同的概率极其微小。
一些缺陷
- 某些场景下,非人类可读是 NanoID 一项缺点
想象一下,一位客户打电话要求提供标识符,然后你必须拼写一个完整的 NanoID ,显然这并不是一种愉快的体验。然而与 UUID 相比,NanoID 可能要短得多、可读性要强一些,但在最终客户需要使用它的情况下仍然无法使用(终归都不太易读)。
- 不能作为任何 SQL 数据表的主键
另外,如果你使用 NanoID 作为表的主键,如果你使用相同的列作为聚集索引,也会出现问题,这是因为 NanoID 是不连续的。具体来看,NanoID(甚至UUID)的本质是随机的,并通过索引按键对记录进行物理排序,因此对于每个插入(如果表上有索引),数据库必须确保也通过这些索引找到新条目,并保持索引顺序和树平衡。
为此,SQL必须对磁盘上的记录重新排序,从而从此索引中删除集群,但是当你有一些连续的东西——比如时间,在插入一个新记录之后进行聚类几乎是无代价的,并且很容易实现。
箴言
软件世界中的任何方法都是主观的,这取决于你的需求进行权衡,并选择适合自己的方案,世界上永远不存在一个设计足够具体、可以永远存在的方案。因此考虑到其中的一些限制,我们选择了某个设计,并且根据它对我们的工作方式的影响,进一步改进它。
👋 感谢您的阅读,祝您学习愉快!