最近我们线上系统发生了一起严重事故:订单号/流水号出现了重复,影响了核心业务流程。最终定位到根源:一个自研的二方包雪花算法ID生成器出现了问题。
下面我们来回顾一下雪花算法的标准结构,分析问题出在哪,并总结一些通用的设计建议。
一、标准雪花算法()
标准的 ID由一个64位long型整数构成:
txt
代码解读
+----------------------------------------------------------------------------------------------------+
| 1 Bit | 41 Bits 时间戳 | 5 Bits 数据中心ID | 5 Bits 机器ID | 12 Bits 序列号 |
+----------------------------------------------------------------------------------------------------+
优点:
二、我们的“定制版”雪花算法:问题在哪?
我们使用的二方包雪花算法结构如下(根据排查推测):
txt
代码解读
+----------------------------------------------------------------------------------------------------+

| 31 Bits 时间戳Delta | 13 Bits 数据中心ID | 4 Bits 工作ID | 8 Bits 业务ID | 8 Bits 序列号 |
+----------------------------------------------------------------------------------------------------+
1. 时间戳仅保留31位,最多支持24.85天!2. 用的是 IP 最后一段3. WorkId 和 未配置,全为0最终结果:时间轮回 + IP冲突 + 序列重复,ID彻底撞车。三、教训总结核心组件不建议自研不盲信二方包合理设置机器ID提前覆盖边界场景四、推荐做法
使用成熟的开源实现,如 Hutool、 等:
java
代码解读
// Hutool 示例
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
long id = snowflake.nextId();
// Baomidou 示例(支持从 IP/MAC 自动推导,也可手动指定)
DefaultIdentifierGenerator generator = new DefaultIdentifierGenerator(1, 1); // workerId=1, dataCenterId=1

long id = generator.nextId("user");
对于中大型系统, 一般用来标识不同的机房或者 AZ ( Zone)。
的配置策略可以根据系统规模逐步演进:
随着系统规模扩大,推荐逐步引入更复杂但更稳妥的机制,避免一开始就过度设计。
五、其它建议:不要将业务标志拼入ID中
有时我们为了确保唯一性,会试图将业务信息(如类型前缀、模块编号)拼接进ID。但这种做法会带来一系列问题:
更稳妥的做法是将业务字段单独存储,ID仅用于唯一标识和排序。
六、结语
别为造轮子而造轮子,尤其在基础组件上,不可抱侥幸心理。
如果你也有雪花算法的踩坑经验,欢迎交流分享!
*请认真填写需求信息,我们会在24小时内与您取得联系。