1. 算法定义与核心思想

Snowflake(雪花算法)是Twitter开源的分布式ID生成算法,用于在分布式系统中生成全局唯一且有序的64位Long型ID。其核心思想是将ID拆分为多个字段,通过组合时间戳、机器标识和序列号确保唯一性与有序性。

2. 算法组成结构

一个典型的64位Snowflake ID结构如下:

字段位数说明
符号位1固定为0(ID为正数)
时间戳41毫秒级时间戳(支持约69年)
数据中心ID5标识数据中心(支持32个)
机器ID5标识同一数据中心下的机器(支持32台)
序列号12同一毫秒内的自增序列(支持每毫秒4096个ID)

示例:
0 | 1623456789000 | 00001 | 00101 | 000000000111

3、用「快递包裹单号」的例子,解释雪花算法:


想象你开了一家快递公司,每天要处理几百万个包裹。每个包裹都需要一个唯一单号,而且要满足:
  1. 绝对不重复(不能有两个包裹用同一个单号)

  2. 能看出时间顺序(单号按时间变大,方便快速整理包裹)

  3. 多个分店同时生成单号(比如北京店、上海店同时工作)

这时候,你会怎么设计单号?


4、雪花算法的解决方案:

单号 = 时间 + 分店编号 + 当天包裹序号

(就像拼积木一样把三部分拼成一个大数字)

具体例子:
假设今天是 2024年1月1日,你有 3个分店(北京店=1,上海店=2,广州店=3),每个分店每秒最多处理 1000个包裹。

生成单号的规则:

  1. 时间部分:记录包裹生成的精确到毫秒的时间戳(比如 09:30:25.500 → 转成一串数字)

  2. 分店编号:每个分店有唯一编号(北京店=1,上海店=2...)

  3. 包裹序号:同一毫秒内,如果分店要生成多个单号,就按 1,2,3... 顺序编号


5、举个真实单号:

1257894001234567891(实际是64位二进制,这里简化成十进制)

拆解这个单号:

  • 前41位(时间):2024年1月1日 09:30:25.500

  • 中间10位(分店):北京店(编号1)

  • 最后12位(序号):当天这个分店在这个时间点生成的第500个包裹


6、为什么这个方案好用?

  1. 绝对不重复:不同时间生成的单号肯定不同;同一时间不同分店生成的单号不同;同一时间同一分店用序号区分

  2. 时间有序:单号整体随时间递增,数据库存包裹时可以直接按顺序堆放,查询更快

  3. 分布式友好:北京店和上海店不需要互相问"你生成到多少号了",各自独立工作


7、对比UUID的快递单号:

如果使用UUID,单号可能是:550e8400-e29b-41d4-a716-446655440000

  • 问题1:完全无序,包裹按单号堆放会东一个西一个,找起来慢

  • 问题2:太长了(36个字符),快递员抄写容易出错

  • 问题3:无法看出是哪个分店、什么时间生成的


8、现实中的使用场景:

  • 电商订单号:2024010112302512345(前段是时间,后段是分店和序号)

  • 聊天消息ID:微信消息按时间顺序排列,快速定位到某条消息

  • 日志流水号:系统故障时,按日志ID顺序排查问题


一句话总结:

雪花算法就像给每个事件打一个「时间戳+地点+流水号」的三合一标签,让计算机世界里的所有事情都有序排队。

9、【注意】

潜在限制

  • 时间回拨问题
    若系统时钟回拨(如NTP同步导致时间倒退),可能生成重复ID。需通过记录上次时间戳并阻塞等待来解决。
  • ID长度固定
    64位长度在某些场景(如需要携带业务信息)可能不足,需结合其他算法(如UUID)或扩展字段。
  • 机器ID分配挑战
    需合理分配数据中心ID和机器ID,避免冲突。大规模集群需预分配或动态管理ID。

改进方案

  • 调整时间戳精度
    将时间戳单位从毫秒改为秒,节约10位扩展序列号,支持每秒生成419万个ID。
  • 引入机器ID预分配
    通过配置中心预分配机器ID,避免手动配置错误。
  • 混合时钟策略
    检测到时钟回拨时,切换至备用时钟生成ID,确保服务可用性

面基:雪花算法Snowflake时钟回拨问题解决方案-CSDN博客

(望各位潘安、各位子健/各位彦祖、于晏不吝赐教!多多指正!🙏)

更多推荐