UUID v4 为什么不会重复?

UUID v4 用 randomUUID()(Node 内置 crypto 模块)生成,格式为 8-4-4-4-12 分组的 36 位字符串,例如 3f2504e0-4f89-41d3-9a0c-0305e82c3301

核心原因

UUID v4 不是绝对保证唯一,而是靠海量随机空间把碰撞概率压到几乎为零

它就像一次抽 122 位的"超级随机数",可能的结果多到什么程度呢?

  1. 取值空间大到离谱:一共 2¹²² 种可能,比地球上所有沙子的数量还多得多。随手抽一个,和别人撞上的概率,比连续中好几次头奖还低。
  2. 不是"伪随机":底层用的是密码学级别的随机(和加密用的同一套),不像 Math.random() 那样有规律可循,所以不会偷偷重复。
  3. 撞一次要等多久:哪怕每秒生成 10 亿个 UUID,也要连续不停生成大约 85 年,才可能出现一次重复。现实中根本到不了这个量级。

什么是"伪随机"(假随机)?

计算机算不出真正的随机,所谓随机数其实是用一个固定公式算出来的:给它一个起始值(叫种子 seed),它就从种子开始一步步往下算,结果看着很乱,但本质是确定、可复现的 —— 种子相同,算出来的一串数就完全一样。这就是"假"的由来:有规律、能被推算。

  • 普通伪随机(Math.random()):种子简单、容易猜,别人能推算出你后面会出什么。
  • 密码学安全随机(randomUUID()):种子来自操作系统采集的真实世界噪声(如硬件事件的纳秒级时刻、CPU 的硬件随机源等),谁也无法复现或猜到,所以生成的值无法被预测、无法被伪造。

这就是 sessionId 要用 randomUUID() 而不是 Math.random() 的原因:后者可能被人推算出来,导致 ID 被冒用。

和时间戳有关系吗?

v4 版本(即 randomUUID())和时间戳没有任何关系,122 位全部是纯随机数,不含时间信息:

  • 同一毫秒生成 100 个,或隔一年再生成,规律完全一样,都是重新抽随机数。
  • 好处:不泄露生成时间、不暴露先后顺序,隐私性好。
  • 代价:生成出来的 ID 是乱序的,无法通过 ID 判断谁先谁后。

不过 UUID 还有别的版本是专门基于时间的:

版本 依据 特点
v1 时间戳 + MAC 地址 含生成时间,但会泄露机器网卡地址
v7 毫秒时间戳 + 随机数 新标准,按时间递增,兼顾有序和随机
v4 纯随机 无时间信息(当前使用的版本)

如果希望 ID 大致按创建时间有序,可以考虑 v7。它对数据库更友好,主要体现在两点:

  • 索引写入更快:数据库主键索引(B+ 树)是按 ID 大小排好序存放的。v7 的 ID 递增,新数据永远追加到末尾,不用挪动老数据;v4 的 ID 随机,新数据会插到中间任意位置,容易触发"页分裂"(挪数据、开新页),写入更慢。打个比方:随机 ID 像往一叠排好序的卡片中间到处硬塞,递增 ID 像一张张往最后叠,后者显然轻松得多。
  • 范围查询更方便:v7 的 ID 本身隐含时间顺序(ID 大 ≈ 创建晚),想查"最近的数据"或"某时间段的数据",直接按主键排序/范围取即可,时间相近的数据在索引里物理位置也挨在一起,扫描快;v4 完全随机,做不到这点,通常还得额外建一个时间字段的索引。

但 Node 内置的 randomUUID() 目前只支持 v4,想用 v7 需引第三方库(如 uuid 包的 v7())。

小结

无需任何人统一分配、本地随手就能生成、几乎不可能重复 —— 所以特别适合用来生成各种唯一标识。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐