UUID 选型指南:v4、v7 该用哪个,碰撞概率到底有多低
讲清 UUID v4、v1、v7 三种版本的区别,为什么 v4 几乎不会碰撞,为什么分布式主键越来越倾向用时序的 v7,以及格式与有序性背后的取舍。
UUID 选型指南:v4、v7 该用哪个,碰撞概率到底有多低
很多人对 UUID 的认知停在「随机生成一个唯一字符串」。真正动手把它当数据库主键用,才会发现版本之间差别不小:选错了,索引会被打散,写入会变慢。下面把 v1、v4、v7 的区别,碰撞概率,以及为什么分布式系统越来越爱用 v7,一次说清楚。
三个版本,三种取舍
UUID 是 128 位的标识符,标准定在 RFC 4122,2024 年又被 RFC 9562 更新。版本号决定这 128 位怎么填:
- v1:前面塞 100 纳秒级时间戳,后面塞生成机器的 MAC 地址。能保证唯一,但把物理机的网卡地址泄露出去了。历史上 Melissa 蠕虫作者就是被 v1 里的 MAC 追溯定位的。RFC 9562 已经把 v1、v2 标记为不推荐。
- v4:几乎全随机。128 位里有 6 位用来标记版本和变体,剩下 122 位是密码学随机数。它不带任何时间或机器信息,也正因如此,谁都猜不出下一个是什么。
- v7:前 48 位是大端序的毫秒级 Unix 时间戳,后面才是随机位。它兼顾了唯一性和时间有序,是 2024 年新标准里专门为数据库主键场景定义的版本。
一个真实的 v4 长这样:
e7c9a4f2-3b8d-4c1a-9f6e-2d0b5a847c13
中间那个 4 就是版本号。换成 v7,这一位会变成 7,而且开头几位 hex 会随生成时间单调递增。
v4 当主键会碰撞吗:把概率算给你看
这是被问得最多的问题。v4 有 122 位随机性,按生日悖论,要生成约 2 的 61 次方(大约 23 亿亿个)UUID,碰撞概率才到 50%。换个直观说法:就算以每秒 10 亿次的速度往里插,也要 70 多年才摸到这个量级。任何真实业务都远远够不着。
所以「会不会撞」在实践中不是问题。稳妥起见数据库层加一个 UNIQUE 约束兜底就行,但你不需要为碰撞本身焦虑。
要警惕的是另一种「短 ID」幻觉:8 位 hex 的 Short UUID 只有 32 位,生日界在约 7.7 万个 ID 就到 50% 了,不是几十亿。它只配做 demo 和短链,千万别拿来当大规模主键。
为什么分布式主键越来越用 v7
自增 ID 在单库时代很好用:新行总是追加到 B-tree 索引的尾部,写入顺滑。可一旦业务拆成多个分库,自增就废了,因为没有一个中心化的计数器能跨库保证唯一。
这时大家先想到 v4。但 v4 是随机的,每次插入都可能落到索引的随机页中间,要拆页,产生写放大,范围查询也跟着变慢。v7 解决的正是这个矛盾:它前 48 位嵌了毫秒时间戳,新记录天然落在索引尾部,效果等同自增主键,同时保持全局唯一,不依赖中心计数器。
而且 v7 的时间戳可以反解。取去掉连字符后的前 12 个 hex,按十六进制解析就是毫秒数。比如 0190b8e5-7c00-7000-8000-000000000000 开头的 0190b8e57c00 换算是 1719331000320 毫秒,大约 2024-06-25。排查问题时不用单独的 created_at 字段也能看出记录大概什么时候写进来,挺顺手。
格式:连字符存不存,看字段类型
我自己踩过一个坑:为了「看着整齐」,把 36 位带连字符的字符串塞进了 varchar 列。后来发现,如果用原生 uuid 类型(PostgreSQL),连字符只是显示用的,底层都是 16 字节,存不存连字符没区别;但存成 text/varchar 时,带连字符是 36 字节,去掉是 32 字节,而真正紧凑的 BINARY(16) 只要 16 字节。大表就该存 16 字节原始值,读取时再格式化。去连字符这个开关,真正的用武之地是 URL、文件名、缓存 key 这些地方,那四个横杠纯属噪声。
怎么选,一句话收尾
需要纯随机、不在乎插入顺序(用户 ID、API Token、会话 ID),用 v4;要当高写入表的主键、希望按时间有序,用 v7;需要一个明确的「无值」占位,用全零的 NIL。想动手生成的话,直接用 UUID 生成器,支持 v4、v7、NIL、Short 四种格式,批量 1 到 1000 个,可去连字符、可下载,全程在浏览器本地跑,不上传任何数据。如果你要的不是标准 UUID 而是更短、URL 友好的 ID,可以看看 nanoid 生成器,键空间和长度都能自己调。
选对版本,主键这件事就不会在半年后变成性能事故。
Made by Toolora · Updated 2026-06-13