介绍一款适合于 URL 的唯一标识符生成器——NaoID

3,199 阅读4分钟

引言

在每个软件系统中,我们都需要唯一的 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必须对磁盘上的记录重新排序,从而从此索引中删除集群,但是当你有一些连续的东西——比如时间,在插入一个新记录之后进行聚类几乎是无代价的,并且很容易实现。

箴言

软件世界中的任何方法都是主观的,这取决于你的需求进行权衡,并选择适合自己的方案,世界上永远不存在一个设计足够具体、可以永远存在的方案。因此考虑到其中的一些限制,我们选择了某个设计,并且根据它对我们的工作方式的影响,进一步改进它。

👋 感谢您的阅读,祝您学习愉快!