UUID、ULID 与雪花 ID:数据库主键到底怎么选

自增 ID 泄露量级、随机 UUID 打散索引、ULID 与雪花有序但各有代价——一张对比表加一个决策框架,把「主键怎么选」从玄学变成三道题。

约 7 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证

先分清四个候选

  • 自增 ID:数据库计数器,短、有序、零依赖;代价是暴露业务量级、分库分表会冲突。
  • UUID v4:128 位随机数(RFC 4122),碰撞概率可忽略、任何机器都能生成;代价是完全无序,B-tree 插入点随机。
  • ULID:128 位 = 48 位毫秒时间戳 + 80 位随机数,Crockford Base32 表示、可排序、大小写不敏感。
  • UUID v7:2024 年进 RFC 9562 的新成员,同样是「时间戳前缀 + 随机尾部」,与 UUID 生态兼容,是 v4 的有序替代。
  • 雪花 ID(Snowflake):64 位 = 时间戳 + 机器号 + 序列号,趋势递增、数值型;代价是要分配机器号、依赖时钟。

一张对比表看全

维度自增 IDUUID v4ULID / UUID v7雪花 ID
长度 / 存储4~8 字节16 字节(36 字符)16 字节(26~36 字符)8 字节
有序性严格递增完全随机毫秒级趋势递增趋势递增(单机严格)
生成方仅数据库任何节点任何节点任何节点(需机器号)
暴露业务信息量级与增速全暴露无暴露生成时间暴露生成时间
索引友好度最好最差(随机写入)好(时间局部性)好
分库分表冲突,需改造天然无冲突天然无冲突需分配机器号段
典型场景单库内部、后台系统对外暴露的实体 ID日志、事件、多数新表高并发订单 / 消息

为什么随机 UUID 伤索引:B-tree 的写入放大

InnoDB 的主键即聚簇索引,行数据按主键顺序物理存放。自增 ID 永远追加到最右页,页写满就开新页——顺序写、缓存命中高。随机 UUID 的插入点均匀散布在整棵树上:每条插入都可能命中冷页,触发读盘、页分裂、缓冲池污染,写入放大直接体现在磁盘 IOPS 上。这也是「时间有序 ID」(ULID / v7 / 雪花)近年流行的根本原因:兼顾无协调生成与索引局部性。

浏览器 / Node:UUID v4 与 v7
crypto.randomUUID();   // v4,随机
// v7 需要库支持(如 uuid@9+ 的 v7 导出):时间有序
雪花 ID 的位布局(64 位)
符号位(1) | 毫秒时间戳(41) | 机器号(10) | 序列号(12)
→ 同一毫秒同一机器最多 4096 个,跨机器永不重复

决策框架:三道题定方案

  1. ID 会给外人看吗?会 → 排除自增(量级泄露、可枚举遍历),选 UUID / ULID / 雪花。
  2. 单库还是会分片?单库小系统 → 自增或 v7 都行;要分片 → 排除裸自增,其余皆可。
  3. 写入量大、在意索引性能吗?是 → 按序取雪花(数值 8 字节最省)或 UUID v7 / ULID(字符串但有序);无所谓 → v4 一把梭。

雪花 ID 的坑:时钟回拨

雪花 ID 的有序性来自机器时钟。NTP 校准让时钟往回跳时,同一机器可能重复发号——轻则报错、重则主键冲突。工程对策:回拨小于阈值就自旋等待,大于阈值拒绝服务并告警;或用「逻辑时钟」兜底。自建雪花服务前先想清楚:多数团队直接用成熟实现(Leaf、UidGenerator),比自己修边界条件划算。

常见问题

UUID v4 做主键,性能真的不可接受吗?

看写入量。每天几万行的表,随机 UUID 的索引开销几乎感知不到;每秒几千次插入的高写入表才会显现差距(页分裂频繁、缓冲池命中率下降)。折中方案是 UUID v7 / ULID:保留无协调生成的优点,同时让插入点集中在索引右侧。

ULID 和 UUID v7 功能几乎一样,选哪个?

生态优先。团队已在 UUID 体系(列类型、ORM、日志格式)里,选 v7 无缝迁移;从零开始且喜欢 Crockford Base32 的紧凑与大小写不敏感,ULID 也完全够用。两者都是「时间戳前缀 + 随机尾部」,性能差异可忽略。

自增 ID 会暴露什么信息?

订单号如果就是自增 ID,竞争者下单两次就知道你的日单量;用户 ID 可枚举遍历,配合越权漏洞就是事故。对外暴露的实体建议「自增主键 + 对外随机 ID」双字段:库内用自增享受索引红利,对外用不可猜的 ID。

分布式 ID 一定要有中心发号服务器吗?

按方案分档:UUID v4 / v7 与 ULID 任何节点本地生成、零协调;雪花 ID 不需要中心「发号」,但每台机器要分配全局唯一的机器号(通常由 ZooKeeper / 配置中心一次性分配);只有「号段模式」(数据库批量取号段)才真正依赖中心点。要无单点又要趋势递增,v7 / ULID / 雪花都能满足。

📂

松开以自动格式化

支持 .json / .sql / .csv / .html / .css / .xml / .yaml 文件