约 7 分钟读完 · 更新于 2026-10-21 · 文中每条都能在下面的配套工具里动手验证
| 维度 | 自增 ID | UUID v4 | ULID / UUID v7 | 雪花 ID |
|---|---|---|---|---|
| 长度 / 存储 | 4~8 字节 | 16 字节(36 字符) | 16 字节(26~36 字符) | 8 字节 |
| 有序性 | 严格递增 | 完全随机 | 毫秒级趋势递增 | 趋势递增(单机严格) |
| 生成方 | 仅数据库 | 任何节点 | 任何节点 | 任何节点(需机器号) |
| 暴露业务信息 | 量级与增速全暴露 | 无 | 暴露生成时间 | 暴露生成时间 |
| 索引友好度 | 最好 | 最差(随机写入) | 好(时间局部性) | 好 |
| 分库分表 | 冲突,需改造 | 天然无冲突 | 天然无冲突 | 需分配机器号段 |
| 典型场景 | 单库内部、后台系统 | 对外暴露的实体 ID | 日志、事件、多数新表 | 高并发订单 / 消息 |
InnoDB 的主键即聚簇索引,行数据按主键顺序物理存放。自增 ID 永远追加到最右页,页写满就开新页——顺序写、缓存命中高。随机 UUID 的插入点均匀散布在整棵树上:每条插入都可能命中冷页,触发读盘、页分裂、缓冲池污染,写入放大直接体现在磁盘 IOPS 上。这也是「时间有序 ID」(ULID / v7 / 雪花)近年流行的根本原因:兼顾无协调生成与索引局部性。
crypto.randomUUID(); // v4,随机 // v7 需要库支持(如 uuid@9+ 的 v7 导出):时间有序
符号位(1) | 毫秒时间戳(41) | 机器号(10) | 序列号(12) → 同一毫秒同一机器最多 4096 个,跨机器永不重复
雪花 ID 的有序性来自机器时钟。NTP 校准让时钟往回跳时,同一机器可能重复发号——轻则报错、重则主键冲突。工程对策:回拨小于阈值就自旋等待,大于阈值拒绝服务并告警;或用「逻辑时钟」兜底。自建雪花服务前先想清楚:多数团队直接用成熟实现(Leaf、UidGenerator),比自己修边界条件划算。
看写入量。每天几万行的表,随机 UUID 的索引开销几乎感知不到;每秒几千次插入的高写入表才会显现差距(页分裂频繁、缓冲池命中率下降)。折中方案是 UUID v7 / ULID:保留无协调生成的优点,同时让插入点集中在索引右侧。
生态优先。团队已在 UUID 体系(列类型、ORM、日志格式)里,选 v7 无缝迁移;从零开始且喜欢 Crockford Base32 的紧凑与大小写不敏感,ULID 也完全够用。两者都是「时间戳前缀 + 随机尾部」,性能差异可忽略。
订单号如果就是自增 ID,竞争者下单两次就知道你的日单量;用户 ID 可枚举遍历,配合越权漏洞就是事故。对外暴露的实体建议「自增主键 + 对外随机 ID」双字段:库内用自增享受索引红利,对外用不可猜的 ID。
按方案分档:UUID v4 / v7 与 ULID 任何节点本地生成、零协调;雪花 ID 不需要中心「发号」,但每台机器要分配全局唯一的机器号(通常由 ZooKeeper / 配置中心一次性分配);只有「号段模式」(数据库批量取号段)才真正依赖中心点。要无单点又要趋势递增,v7 / ULID / 雪花都能满足。