编辑
2026-06-04
java炒饭
00
请注意,本文编写于 79 天前,最后修改于 79 天前,其中某些信息可能已经过时。

目录

一、原理:64位ID的精细划分
生成流程(简化):
二、优势
三、劣势与挑战

下面解析雪花算法(Snowflake) 的核心原理,以及它的优势与局限。

这是Twitter在2010年开源的分布式ID生成算法,旨在解决分布式系统中需要全局唯一、趋势递增、高性能的ID生成问题。它的输出是一个64位的long型整数,结构清晰,非常经典。


一、原理:64位ID的精细划分

雪花算法的64位(bit)被划分为以下几个部分(从左到右,高位到低位):

位数字段名含义与作用
1位符号位固定为0。因为ID是正数,最高位为0。
41位时间戳记录当前时间与某个起始时间的差值(毫秒级)。可支持约69年。
10位工作机器ID用于区分不同的节点/机器,最多支持1024个节点(2^10)。实际常拆分为5位数据中心ID + 5位机器ID。
12位序列号同一毫秒内生成的不同ID的序号,支持每毫秒每节点最多4096个ID(2^12)。

整体结构示意图:

0 | 41位时间戳(毫秒) | 10位机器ID | 12位序列号

生成流程(简化):

  1. 获取当前时间戳(毫秒)。
  2. 如果当前时间戳 等于 上一次生成ID的时间戳:
    • 序列号自增1。
    • 若序列号超过4095,则等待下一毫秒,并将序列号重置为0。
  3. 如果当前时间戳 大于 上一次的时间戳:
    • 序列号重置为0。
  4. 如果当前时间戳 小于 上一次的时间戳(即时钟回拨),则算法通常停止生成或等待。
  5. 将各部分按位或(左移后相加)组合成一个64位long整数。

示例(假设起始时间为2010-11-04 09:42:54.657):

  • 时间戳差值:某个毫秒值
  • 机器ID:1
  • 序列号:5 → 最终得到一个类似 1456192189189701632 的ID。

由于时间戳在高位,生成的ID趋势上是递增的(但不严格连续,同毫秒内序列号连续)。


二、优势

  1. 全局唯一,分布式友好
    只需确保每个节点拥有唯一的机器ID,就可以独立生成ID,无需中心化协调。非常适合微服务、分库分表场景。

  2. 趋势递增,利于数据库索引
    生成的ID在毫秒级时间上递增(同毫秒内序列号递增)。对数据库B+树索引非常友好,减少页分裂,写入性能高。

  3. 高性能
    生成一个ID仅需几次位运算和内存操作,无网络IO、无锁竞争(单节点内常用atomic实现)。单机轻松达到数十万甚至百万级QPS

  4. 灵活性
    位数分配可按需调整。例如增加机器位或序列号位,适应不同规模集群。

  5. 无外部依赖
    算法只依赖当前机器的时间戳,不依赖数据库、ZooKeeper、Redis等第三方服务,轻量可靠。


三、劣势与挑战

  1. 强依赖机器时钟
    如果节点的系统时钟发生回拨(向前调整时间),那么有可能生成重复ID或导致新ID小于已生成的ID。

    • 常见原因:NTP同步、人为修改、网络延迟等。
    • 处理方案
      • 检测到回拨时:直接抛出异常阻塞等待使用备用时钟源
      • 更健壮的方案(如百度UidGenerator):使用未来时间生成策略 + 回拨容忍。
  2. 机器ID的分配与管理问题
    每个节点需要独立的机器ID(10位)。如何安全、动态地分配这1024个ID而不冲突?

    • 简单方案:手动配置。
    • 常见方案:使用ZooKeeper、etcd或数据库自增表做中心化分配。
    • 在容器或弹性伸缩场景下,自动分配ID是个难题。
  3. 时间跨度有限
    41位时间戳(毫秒级,起始点固定)可用约69年。如果服务持续运行超过69年,需要变更起始时间或扩展位数(通常业务系统远达不到这个时限)。

  4. 序列号容量限制
    每毫秒每节点最多4096个ID。若瞬时并发超过这个量,可能产生少量等待(自旋到下一毫秒)。但在绝大多数场景下足够。

  5. 无法保证全局严格递增
    “趋势递增”≠“单调递增”。不同节点由于时钟差异,可能会出现后生成的ID比先生成的ID数值小的情况(跨节点比较)。如果要求所有ID全局严格递增,雪花算法不满足。


如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay