用NanoID换掉 UUID,好处是?

当我们在分布式环境中存储一些数据的时候,不得不面对的一个选择,就是ID生成器。

使用一个唯一的字符串,来标识一条完整的记录。

这时候,不能使用md5或者sha1来对整个记录做摘要,因为我们后续还要改动这个记录。也不能使用单机的计数器,因为计数器容易重启清零,也会存在多台机器上的数值重复,这违背了无状态服务的建设目标。

无奈的选择UUID

虽然UUID在大多数语言中都有相关的类库,但除非迫不得以,我们一般不会使用它。UUID虽然不会重复,但它非常的长,长的让人望而生畏。

XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

标准的UUID有5个部分组成:8-4-4-4-12,一共32个十六进制字符。因此,一共是128位。

图片

当把UUID作为数据库的索引时,会因为它没有顺序性造成索引的随机分布和;因为数据量巨大造成查询性能降低。

同时,UUID也是不可读的。如果你把它打印在纸质的订单上,并不是一个好的主意。

UUID同时还有信息安全的隐患,它的数据计算里有MAC地址的参与,比较知名的是,曾被用于寻找梅丽莎病毒的制作者位置。

改造时间戳

如果你是单机应用,那么使用时间戳没什么问题,即使不用纳秒,使用毫秒也是足够的。但在分布式环境下面,时间戳同样不是一个好的选择。

即使你在机器安装了ntpd时间同步,但由于网络和机器的差异,计算机的时钟总是存在差异,你的时间戳总会出现重复。为了解决这个问题,你需要增加一些其他的标识,比如机器的ID,或者更多细分的信息减少时间的碰撞。

这种自定义的ID生成器,只适合特定的业务。

做着做着你就会发现,它本质上是雪花算法的变种。

雪花算法

雪花算法生成的ID是long类型,默认字符串长度是19位,它分为4个部分。

图片

  1. 保留位 1 位。

  2. 毫秒时间戳 — 41 位(比如从现在开始,支持到未来的69年),这个其实也挺尴尬的,因为70年之后就会失效。但你不会在一家公司工作70年,所以,随它去吧。

  3. 配置的机器/节点/分片 ID — 10 位(总共支持 2^10 = 1024 个节点)

  4. 序列号 - 12 位(机器的本地计数,所以支持的并发已经很高了)

相比起UUID来,雪花算法所生成的ID是排序的,具有更好的紧凑性,是目前大多数业务优先采用的ID生成算法。

值得注意的是,雪花算法在JavaScript中有一个坑。后端在返回ID的时候,需要使用String类型代替Long类型,否则会产生预想不到的错误。

这是因为。在JavaScript中,存在两种数字。Number和BigInt。最常用的,就是number。

最大的Number,叫做Number.MAX_SAFE_INTEGER,它的值为:

  • 2^53-1 或者

  • +/- 9,007,199,254,740,991

众所周知,Java中的Long,是64位的。Js中的这个安全Integer,完全达不到Java中定义的长度。

这就是万恶的IEEE_754规范,它在Long长度大于17位时会出现精度丢失的问题。

NanoID

NanoID是从JavaScript库中演变过来的,目前在多个语言上通用。它长下面这样。

V1StGXR8_Z5jdHi6B-myT

图片

虽然NanoID无法替代雪花算法,但就凭这长度,替换UUID是绰绰有余的。NanoID 大小只有 108 字节,比UUID小了35%,更加紧凑。

另外,它的速度更快,它可以使用默认字母表每秒生成超过 220 万个唯一 ID,使用自定义字母表时每秒可以生成超过 180 万个唯一 ID,且几乎没有碰撞几率。

如果你的ID对顺序性没有什么严格的要求,比如使用了kv等非常松散的数据库,那么NanoID是你的不二选择。

### UUID 的概念及其生成方法 #### 什么是 UUIDUUID 是 Universally Unique Identifier 的缩写,表示全局唯一标识符。它是一种标准的标识符格式,用于在分布式环境中生成唯一的标识码[^1]。 #### GUID UUID 的关系 GUID(Globally Unique Identifier)是 UUID 的另一种称呼,在某些场景下两者可以互换使用。然而,在实际开发中,GUID 更多指的是微软实现的一种特定形式的 UUID。 --- #### UUID 的版本分类 根据 RFC 4122 标准,UUID 被分为五个主要版本,每种版本都有其独特的生成方式: - **版本 1 (时间戳)** 基于当前的时间戳节点 ID(通常是 MAC 地址)来生成 UUID。这种类型的 UUID 可能会暴露设备的信息,因此存在一定的隐私风险[^3][^1]。 - **版本 2 (DCE 安全)** 这是一个较少使用的版本,主要用于 POSIX 用户的安全认证环境。它的结构类似于版本 1,但加入了额外的身份验证字段。 - **版本 3 (命名空间与 MD5 散列)** 使用指定的命名空间标识符名称作为输入,通过 MD5 散列算法生成固定长度的 UUID。由于基于固定的输入数据,重复调用会产生相同的 UUID。 - **版本 4 (随机数)** 利用伪随机数生成器创建完全随机的 UUID。这是最常用的版本之一,因为它不需要依赖外部信息即可生成独立的标识符^。 - **版本 5 (命名空间与 SHA-1 散列)** 类似于版本 3,但它采用更安全的 SHA-1 散列函数替代了 MD5。同样地,给定相同的名字命名空间时,产生的 UUID 不会发生变化。 --- #### 如何生成 UUID? 以下是几种常见的编程语言生成 UUID 的示例代码: ##### Python 中生成 UUID Python 提供了一个内置库 `uuid` 来轻松生成不同版本的 UUID: ```python import uuid # 随机生成一个版本 4 的 UUID random_uuid = uuid.uuid4() print(f"Version 4 Random UUID: {random_uuid}") # 基于字符串生成版本 3 或者版本 5 的 UUID namespace = uuid.NAMESPACE_URL # 内置命名空间 name = "example.com" version_3_uuid = uuid.uuid3(namespace, name) version_5_uuid = uuid.uuid5(namespace, name) print(f"Version 3 UUID: {version_3_uuid}") print(f"Version 5 UUID: {version_5_uuid}") ``` ##### JavaScript 中生成 UUID JavaScript 并未提供原生支持,但可以通过第三方库或者自定义逻辑完成: ```javascript // 自定义简单版 Version 4 UUID 生产器 function generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } console.log(generateUUID()); ``` ##### Java 中生成 UUID Java 提供了强大的工具类 `java.util.UUID` 支持多种操作: ```java import java.util.UUID; public class Main { public static void main(String[] args) { // 创建一个新的随机 UUID 对象 UUID randomUUID = UUID.randomUUID(); System.out.println("Random UUID: " + randomUUID); // 获取 UUID 各部分属性 long mostSigBits = randomUUID.getMostSignificantBits(); long leastSigBits = randomUUID.getLeastSignificantBits(); System.out.println("Most Significant Bits: " + mostSigBits); System.out.println("Least Significant Bits: " + leastSigBits); } } ``` --- #### 查看 UUID 的教程资源 如果希望深入了解 UUID 的具体应用场景技术细节,可以从以下几个方面入手: 1. 学习官方文档或技术规范——例如阅读 [RFC 4122](https://tools.ietf.org/html/rfc4122),它是关于 UUID 设计的核心参考资料。 2. 实践练习:尝试编写程序生成各种版本的 UUID,并观察它们的特点[^2]。 3. 掌握常见框架中的集成方案:许多现代 Web 开发框架都提供了便捷的方法处理 UUID 数据类型[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值