995 字
约 3 分钟
9
S17-J05 · 分布式 ID:雪花、号段与时钟回拨

适用读者:被"ID 重复"或"ID 太长"困扰的人。
前置:S11-A01(自增主键)、S17-J04。
实测环境:Python 参考实现(结构验证),2026-11-05。
覆盖旧文:—(E 驱动;旧文未覆盖分布式 ID)
覆盖扩展:E-S17-009(雪花结构)、E-S17-010(时钟回拨)。
上一篇:S17-J04 · 分布式事务:2PC / TCC / Saga / 本地消息表
下一篇:S17-A01 · 服务治理。


1. 一句话结论

雪花 ID = 时间戳(41 位)+ 机器号(10 位)+ 序列号(12 位)。它靠"时间递增"保证趋势有序,所以时钟回拨会让它重复或倒序**。**

| 41 bit 毫秒时间戳 | 10 bit workerId | 12 bit 序列号 |

2. 结构(E-S17-009)

段 位数 容量
时间戳(相对纪元) 41 约 69 年
机器/数据中心 10 1024 个节点
序列号 12 每毫秒 4096 个

理论吞吐:4096 × 1000 = 409.6 万/秒/节点。

实测(Python 参考实现,仅验证结构与序性):

生成 1000000 个雪花 ID:1632.5 ms,约 613 千/秒
单调递增: True   无重复: True
示例 ID: 393884553522974720   位长: 59

注意:Python 吞吐不代表生产实现(Java 可达数百万/秒);这里只证明单调性与唯一性在"同毫秒内序列号用尽 → 等下一毫秒"的实现下成立。


3. 为什么不用 UUID / 数据库自增

方案 问题
UUID 无序(B+ 树插入随机,S11-B05 的页分裂)
数据库自增 单点、分库后冲突、暴露业务量
雪花 趋势递增、无中心、可排序

"趋势递增为什么重要":MySQL 聚簇索引按主键有序存储——有序 ID 让插入集中在尾部,避免随机 IO。


4. 时钟回拨(E-S17-010)

原因 后果
NTP 校时回退 可能生成重复 ID
人工改时间 同上
闰秒/虚拟化时钟漂移 顺序错乱

四种处理:

方案 说明
检测并等待 回拨小于阈值就等(简单,短暂不可用)
用备用 workerId 换节点(需协调)
追加回拨计数位 牺牲位宽换可用
用号段模式 规避时钟问题(数据库/Redis 发号)

判据:"节点时钟受控吗?" 不受控 → 用号段(美团 Leaf 类方案),别硬上雪花。


5. 号段模式:另一种主流

DB/Redis 存 max_id → 应用一次取 1000 个号段 → 内存递增发号
优点 代价
不依赖时钟 需中心存储(但可多实例)
ID 短、有序 重启会跳号

6. 五个误区

误区 真相
"雪花 ID 一定不重复" 时钟回拨会重复
"UUID 更适合分布式" 无序导致写放大
"位宽越大越好" 64 位是常见上限(bigint)
"号段模式不会跳号" 重启会浪费一段
"用时间戳当 ID 就行" 并发下会重复

7. 自检

  1. 雪花的三段位宽与容量?
  2. 理论吞吐怎么算?
  3. 时钟回拨为什么会导致重复?
  4. 四种处理方案?
  5. 什么时候该用号段而不是雪花?

8. 下一篇

S17-A01 · 服务治理:限流、熔断、降级

ID 讲完,进架构师层:一次故障为什么会扩散成雪崩。


本篇属于系列《S17 · 分布式与消息队列》。扩展点 E-S17-009/010。实测为 Python 参考实现(结构与序性验证,吞吐不代表生产)。

S17-J05 · 分布式 ID:雪花、号段与时钟回拨
https://www.clxhxhhr.top/posts/5428/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。