每个开发者都曾把 3f8a2c1e-9b4d-4e7a-a1b2-c3d4e5f6a7b8 这样的 UUID 直接塞进表结构而不假思索。但 UUID 有多个版本、特性差异很大,选错版本会在数据量上来后悄悄拖垮数据库性能。这篇文章讲清楚真正重要的部分。
UUID 到底是什么
UUID(通用唯一标识符)是 RFC 9562(旧称 RFC 4122)定义的 128 位值,标准文本形式把 16 个字节按 8-4-4-4-12 分组写成十六进制。这 128 位并非全是随机数——其中几个位用来标记版本(生成方案)和变体。如果第三组开头是 4、第四组开头是 8/9/a/b,你看到的就是一个 v4 版本的 RFC 标准变体。
版本一览
v1 —— 时间戳 + MAC 地址。 最早的方案,把时间戳和网卡 MAC 编进 ID。优点是按时间有序,缺点是暴露生成机器的身份,存在隐私风险,今天很少作为默认选择。
v4 —— 完全随机。 128 位中有 122 位来自密码学安全的随机源。ID 不携带任何时间或来源信息,是日常默认版本,也是本站 UUID 生成器生成的类型。
v7 —— 时间有序随机。 2024 年标准化:高位放毫秒级 Unix 时间戳,其余用随机数填充。兼顾 v4 式的不可预测性和近似按时间排序的特性。
v3 / v5 —— 基于名称。 由命名空间 + 名称确定性哈希而来(v3 用 MD5、v5 用 SHA-1)。相同输入永远得到相同 UUID,适合需要”可复现 ID”的场景,比如从外部键派生稳定标识。
“唯一”到底有多唯一
v4 携带 122 个随机位。常被引用的数学结论:生成约 2⁶¹(约 2700 万亿)个 UUID 之后,出现任意一次碰撞的概率仍只有十亿分之一左右。生产系统中从没有过”意外 v4 碰撞”造成的事故——人们说的”UUID 撞了”几乎都是在描述 bug(复用了坏的随机源、或截断了 ID),而不是数学问题。
数据库索引问题
版本选择真正产生影响的地方在这里。多数数据库(MySQL InnoDB、SQL Server)按主键物理聚簇存行。随机 v4 主键意味着每次插入都落在不可预测的页上,导致:
- 已满的 16KB InnoDB 页频繁页分裂,索引碎片化
- 缓冲池膨胀,因为”最近访问页”的工作集变成了整个索引
- 每次分裂都要写 redo 日志,写放大严重
大表上的基准测试里,v4 主键的插入速度常常比单调键慢数倍。两个对策:
- 用 v7(或 ULID)——时间有序,插入近似追加写,页按顺序填满。
- 保留自增内部主键,UUID 作为二级唯一列——对外只暴露 UUID,各节点无需协调即可生成 ID。
实用选型法则
- 对外暴露的标识符:用 UUID(v4/v7)。自增整数会泄露业务量——竞品仅凭订单号就能估出你的单量。
- 大规模数据库主键:v7 或自增;聚簇索引上避免裸 v4。
- 幂等键、去重键:v4 完美适用——不可预测性防止猜出其他客户端的键。
- 从自然键派生可复现 ID:v5 + 专用命名空间。