连载中 2/16

UUID:最顺手的瑞士军刀,最差的订单主键

2026-06-06 · 5535 阅读 · 0 评论 · 0 赞

事故后的第一反应

退款事故复盘会上,同事小刘第一个举手:换 UUID 啊!本地生成、不用协调、概率上全球不重复,一行代码的事。老王先点头——这确实是分布式 ID 的第一候选——然后打开订单表的空间监控:如果真换了,订单主键索引要涨三倍多。UUID 是好东西,但好东西也要看放哪。这篇把它的功与过拆清楚。

先认清这把刀

UUID 是 128 位的标识符,标准写法 36 个字符,按 8-4-4-4-12 分段:550e8400-e29b-41d4-a716-446655440000。它是个家族,不是一种东西:

版本怎么生成特点
v1时间戳加 MAC 地址有序但泄露机器 MAC,时序字段在高位尾部
v3 / v5命名空间加名字做哈希同名同号,可用于幂等去重
v4纯随机 122 位最常用,完全无序
v7毫秒时间戳打头加随机尾部趋势有序,2024 年进 RFC 9562 的新宠

Java 里 UUID.randomUUID() 用的就是 v4。它的核心优势一句话:发号不依赖任何人——没有 DB、没有 Redis、没有协调服务,本机生成,吞吐只受 CPU 限制。122 位随机空间,一纳秒生成一个也要上百亿年才有可观碰撞概率,唯一性靠概率而非协调,这是它和后面所有方案的本质区别。

当 InnoDB 主键的三重暴击

优势讲完,泼三盆冷水。第一盆:无序写,页分裂狂魔。InnoDB 是聚簇索引,数据按主键物理有序存放。自增 ID 永远往最后一页追加;v4 UUID 随机落点,新行可能插到任何一页——页满了就分裂、挪数据、维护父节点,写放大直接拉满。写入吞吐掉一个量级不是夸张,是 MySQL 系列里页分裂机制(第 2 篇)的必然结果。

第二盆:太肥,索引全面膨胀。bigint 主键 8 字节,UUID 存成 char(36) 是 36 字节起步。更要命的是 InnoDB 的二级索引叶子节点都存着一份主键值——订单表十几个二级索引,每个都要跟着胖。老王算过账:一亿行的订单表,主键从 bigint 换成 UUID,光索引空间多出几个 GB,缓冲池里能缓存的页少了,命中率跟着掉。索引是一寸金一寸土的地方。

第三盆:语义为零。随机串没有时间信息、没法排序、没法范围查询,按时间拉订单还得回表查别的列。运营说「把昨晚八点后的订单拉出来」,自增 ID 和雪花 ID 都能走主键范围,UUID 只能全表筛。

v7 与 ULID:改良,但没根治

社区早就看穿了无序的痛点,给出两条改良路线:UUID v7 把毫秒时间戳放在最高位,整体趋势有序,插入落点集中在 B+ 树右侧,页分裂大幅缓解;ULID 同思路,128 位里时间戳占 48 位,剩下的随机,还做了大小写友好的 Crockford 编码。两者都值得收录进工具箱。

但注意改良的边界:时间戳前缀牺牲了一点随机性(同一毫秒内的序靠随机尾部维持),并且长度问题纹丝没动——128 位还是 128 位,索引还是那么肥。所以 v7 适合替代 v4 的场合,不适合替代 bigint 主键的场合。

场景判断:什么时候用,什么时候别用

场景用不用理由
链路追踪 traceId全链路透传,不落库索引,无序无所谓
幂等键、去重键v3/v5 同名同号的特性正好
离线数据同步冲突避免两端各自生成不打架
订单表主键别用无序写页分裂加索引膨胀
需要按时间范围检索的表别用 v4语义为零,范围查询全靠回表

小结

UUID 一句话:解决唯一性的成本最低,但把成本转嫁给了索引和写入。它是最顺手的瑞士军刀——traceId、幂等键、离线同步样样精通;当订单主键却是三重暴击。数据库主键需要的是趋势递增,这就引出下一步:继续让数据库发号,但换一种聪明的方式——号段模式。

503

10 年全栈工程师 · 503咖啡馆主理人

#分布式ID#UUID#ULID#主键#页分裂

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞