三个常用版本对比
| 版本 | 组成 | 特点 | 适用 |
|---|---|---|---|
| v1 | 时间戳 + 节点(MAC) | 大致有序,但泄漏时间与设备信息 | 内部、需顺序追踪的历史系统 |
| v4 | 122 位随机 | 最常用,无顺序 | 通用唯一标识 |
| v7 | 时间戳前缀 + 随机 | 可按时间排序,索引友好 | 数据库主键、日志 ID |
碰撞概率有多大
v4 需要生成大约 2.7×10¹⁸ 个UUID 才有 50% 的概率出现一次碰撞。也就是说在真实业务规模下,“重复”几乎不是问题——真正的痛点是无序。
做主键的性能陷阱
随机 UUID 会让 B+ 树索引随机插入,带来页分裂与写放大;数据量增长后,插入性能与缓存命中率都会下降。解决办法是让 ID 保持时间局部性:用 v7 / ULID,或直接用数据库自增、雪花 ID。已有大量 v4 主键的系统,也可以保留 UUID 作为对外标识、内部另设顺序主键。
三个不该用 UUID 的场景
- 需要短、可口头传播的标识:邀请码、短链用 UUID 太长,应使用更短的编码(如 Base62 随机串);
- 需要人工识别与排错:订单号这类编号用有语义的规则更好读;
- 当作安全令牌:见下。
常见问题
UUID 能当密码或令牌吗?不能。v4 只有 122 位随机,且没有有效期、作用域与撤销语义;令牌应使用专门的方案(随机 256 位 + 服务端校验)。UUID 会泄漏信息吗?v1 会泄漏生成时间与设备 MAC;v7 会泄漏生成时间(通常可接受);v4 不泄漏任何结构信息。
ID 方案怎么选
| 方案 | 有序性 | 长度 | 适用 |
|---|---|---|---|
| 数据库自增 | 天然有序 | 小 | 单库、内部系统 |
| 雪花 ID | 时间有序 | 64 位整数 | 分布式、需要紧凑 ID |
| UUID v4 | 无序 | 36 字符 | 通用唯一标识、对外暴露 |
| UUID v7 / ULID | 时间有序 | 36 / 26 字符 | 数据库主键、日志追踪 |
生成与使用建议
- 用系统或标准库生成(
crypto.randomUUID()、各语言的 uuid 库),不要用Math.random()拼——它不是密码学安全的; - 存储用 16 字节二进制或去掉连字符的 32 字符形式,比 36 字符字符串更省空间、比较更快;
- 需要按时间查询,就让 ID 自带时间序(v7 / ULID);否则额外加一列创建时间并建索引;
- 对外暴露前评估信息泄漏:v1 带时间与 MAC,v7 带创建时间,v4 不带任何结构信息。
实战案例:主键迁移的三条经验
- “换成 UUID 后写入变慢”:v4 随机主键导致 B+ 树页分裂。解决:改用 v7/ULID,或保留自增主键、UUID 仅作对外标识。
- “对外暴露的 ID 被人推断出规律”:v1 含时间与 MAC,可能暴露生成时间与设备。解决:对外统一用 v4,至少不要暴露 v1。
- “日志想按时间排序,UUID 却排不了”:v4 无顺序。解决:日志 ID 用 v7/ULID,天然按时间有序,便于检索与分页。
常见问题(FAQ)
UUID v4 会重复吗?概率极低(约需 2.7×10¹⁸ 个才有 50% 概率碰撞一次),但仍建议数据库加唯一约束兜底。UUID 能当会话令牌吗?不建议,它没有有效期、作用域与撤销语义;令牌应用 256 位随机串并结合服务端校验。去掉连字符会更省吗?更省空间也更快比较,只是可读性略差;存储常存 16 字节二进制。ULID 和 v7 该选哪个?都能按时间排序——ULID 更短(26 字符,Crockford Base32),v7 是标准 UUID 格式(36 字符);按团队与生态习惯选即可。
动手试试:UUID 生成
各版本一览
- v1:时间戳 + MAC 地址,可排序但可能泄漏机器信息;
- v3 / v5:基于命名空间与名称的确定性哈希(v3 用 MD5、v5 用 SHA-1),同名必得同值,适合做稳定映射键;
- v4:122 位随机,隐私最好,但无序导致索引随机写;
- v6 / v7:新标准,把时间放在前部以获得可排序性,v7 与 Unix 毫秒时间戳对齐,适合做主键;
- 选型口诀:需要排序用 v7/ULID,需要确定性映射用 v5,只要随机不重复用 v4。
迁移与共存
从自增主键迁移到 UUID 主键代价很高(索引重建、外键调整),更稳妥的路径是保留原主键、新增一列对外标识并回填历史数据,等上下游全部切换后再考虑是否调整物理主键。任何一次只改一半的迁移都会造成数据对不上。