UUID、雪花算法、号段模式怎么选?
简化版
UUID 适合去中心、跨系统先生成和外部不可预测标识,但字符串长且 UUIDv4 随机写索引差;Snowflake 是 64 位、趋势递增、吞吐高,但依赖 WorkerId 和时钟;号段模式是 64 位且不依赖时钟,需中心数据库分段并接受空洞。 选型要看字段长度、索引局部性、是否离线生成、中心依赖、跨地域和安全暴露,而不是只比较理论 QPS。
详细版
数据库内部有序 bigint 主键且能维护中心分配时,号段通常简单稳健;节点多、要求本地高吞吐且能治理时钟/租约时选 Snowflake;客户端或多组织无需协调创建、或外部 ID 需不可猜时用 UUIDv4/UUIDv7,内部可另保留 bigint 主键。
UUIDv7 等时间有序 UUID 改善索引局部性,但仍为 128 位且不自动提供业务顺序。Snowflake 暴露时间/节点并存在寿命规划;号段会因实例重启浪费号码且生成顺序不严格递增。最终以真实数据库写入、索引大小和故障演练验证。
完整版教学
一、先列需求而不是先选算法
需要回答:ID 在服务端还是客户端生成?数据库是 bigint 还是 UUID 原生类型?是否要求外部不可猜?是否容忍中心短时不可用?是否需要大致有序?多机房能否协调?同一方案在内部主键和公开 URL 上可能有不同答案。
记忆钩子:没有“最强 ID”,只有对你的生成位置、存储和故障模型最合适的 ID。
二、三类方案的核心机制
| 方案 | 位宽/形态 | 协调依赖 | 顺序性 | 主要风险 |
|---|---|---|---|---|
| UUIDv4 | 128 位随机 | 无 | 无 | 索引随机、空间大 |
| UUIDv7 | 128 位时间有序 | 无 | 趋势递增 | 时钟/隐私、空间大 |
| Snowflake | 常见 64 位 | Worker 租约 | 趋势递增 | 回拨、worker 冲突 |
| 号段 | 常见 64 位 | 低频中心 DB | 区间递增 | 中心高水位、空洞 |
不同 UUID 版本差异很大,不能用 UUIDv4 的随机索引特征概括 UUIDv7。
三、UUID 的优势与代价
UUIDv4 可在浏览器、移动端和多个组织独立生成,122 位随机空间使碰撞概率在合理规模下极低,但不是数学绝对零;数据库仍应有唯一约束。16 字节二进制存储优于 36 字符文本,但比 8 字节 bigint 扩大主键和二级索引。
UUIDv7 将毫秒时间放高位,改善 B+Tree 局部性。它仍不保证跨节点真实先后,且暴露大致生成时间。
四、Snowflake 的容量与运维
经典 41+10+12 布局支持约 69.7 年、1024 workers、每 worker 每毫秒 4096 序列。64 位整数适合数据库索引,生成完全本地。但 WorkerId 必须租约唯一,时钟回拨必须停发/等待/安全处理,位布局到期还要迁移。
单 worker 理论上限 = 2^12 × 1000 = 4,096,000 IDs/s
集群理论上限 = 单 worker 上限 × 活跃 worker 数
这是位布局上限,不是业务承诺值;锁竞争、语言运行时停顿和下游数据库吞吐通常会先成为瓶颈。
它适合成熟基础设施团队,不适合把 worker 配置写死后无人治理的环境。
五、号段的性能与故障模型
数据库一次原子更新分配 [start,end],实例内存递增,双 buffer 提前预取。步长 100000、速率 5000/s 时单段约 20 秒,可容忍中心短时故障。实例宕机丢弃余号产生空洞,但唯一性安全。
中心数据库从旧备份恢复可能回退 max_id,是号段特有高风险;恢复要跳过历史已分配上界。
六、数据库与外部接口的组合选型
MySQL 聚簇主键常用 bigint Snowflake/号段保持较好局部性,另设随机 public_id 暴露给外部。PostgreSQL 对 UUID 支持较好,也仍需测索引大小和写放大。不要为了“全局统一”强迫所有边界共用一个 ID。
例如内部订单 8 字节号段 ID,外部使用 UUIDv7/public token;两者唯一索引关联。这样兼顾存储效率与不可枚举性,代价是一列和一次映射。
七、常见误区与追问
- 误区:UUID 永远不会碰撞,所以不需要唯一索引。 概率极低不等于无需防御,生成器也可能有 bug。
- 误区:所有 UUID 都导致随机索引。 UUIDv7 等时间有序版本特征不同。
- 误区:Snowflake 完全去中心化。 WorkerId 分配和时间治理仍需控制面。
- 误区:号段模式每次都访问数据库。 只有申请新段时访问,发号在内存完成。
- 追问:哪个吞吐最高? 三者本地生成都可很高,真实瓶颈常在数据库写入和索引,应实测。
- 追问:外部订单号选什么? 优先随机不可猜 public_id,并保留严格资源鉴权。
- 追问:不允许空洞怎么办? 这是票据/业务序号问题,需要串行提交与作废审计,不宜拿普通主键方案硬做。
八、加强记忆
UUID 强在无协调和外部随机,Snowflake 强在 64 位本地趋势递增,号段强在不依赖时钟且中心访问低频。选型沿“生成位置、存储宽度、索引顺序、协调故障、时钟、公开安全”六个维度比较,必要时内部主键与外部 ID 组合使用。
如果业务跨语言和数据库,还要验证无符号数、JSON 大整数精度、UUID 字节序和排序规则;算法正确并不代表传输链路不会改值。
压测应使用真实表结构与二级索引,比较数据文件、索引文件、页分裂和 P99 写延迟,而不是只测生成函数每秒能返回多少个值。
迁移方案通常采用新列双写、历史回填、读路径切换和旧列下线四步,并在每一步保留唯一约束与回滚能力。