snowflake算法(雪花算法)讲解

Snowflake算法(雪花算法)是一种由 Twitter 提出并开源的 分布式唯一ID生成算法 ,其主要目的是在分布式系统中生成 全局唯一的、有序的、高性能的ID。这类ID通常用于数据库主键、订单号、消息ID等场景,替代数据库的自增ID,避免了分布式环境下ID冲突问题。


一、Snowflake ID 组成结构

Snowflake 生成的是一个 64位的长整型ID,一般结构如下(从高到低):

复制代码
0 - 41位时间戳 - 10位机器信息 - 12位序列号
位数 名称 含义
1 符号位 恒为0,正数(因为是Long型)
41 时间戳部分 相对于某个起始时间的毫秒数
10 机器位 记录生成ID的机器信息
12 序列号部分 每毫秒生成的ID序号(0-4095)

二、详细说明各部分

1. 时间戳(41位)

  • 单位是毫秒。

  • 通常是"当前时间戳 - 起始时间戳(epoch)"的差值。

  • 41位最多可表示约 69年

    scss 复制代码
    2^41 / (1000 * 60 * 60 * 24 * 365) ≈ 69年

2. 机器信息(10位)

  • 分为两部分(常见方案):

    • 5位数据中心ID:最多支持 2⁵ = 32 个数据中心。
    • 5位机器ID:每个数据中心最多 2⁵ = 32 台机器。

3. 序列号(12位)

  • 同一毫秒内,同一台机器最多可生成 2¹² = 4096 个唯一ID。
  • 如果超过,会等待到下一毫秒。

三、Snowflake的优点

优点 说明
全局唯一 由时间 + 机器 + 序列号组成,不重复
趋势递增 时间戳在高位,生成的ID大致是单调递增的
高性能 本地生成,不依赖数据库等第三方,QPS可达百万级别
分布式友好 支持多个节点独立生成,不需要中心协调器

四、Snowflake ID 示例

以 Java 版为例,生成的ID:

java 复制代码
ID = 0 | 时间戳差值(41位) | 数据中心ID(5位) | 机器ID(5位) | 序列号(12位)

例如生成:

text 复制代码
1480166465631(当前时间戳)
机器ID = 1
数据中心ID = 1
序列号 = 0

转换为二进制拼接后,得到一个64位的long类型ID。


五、常见问题

Q1:为什么不直接用 UUID?

  • UUID 长度为128位,不适合做主键(索引效率低)
  • UUID 无序,不利于数据库分页、排序
  • UUID 相比 Snowflake 的 ID 更大,存储空间浪费

Q2:Snowflake 时间回拨怎么处理?

时间回拨是指系统时钟出现错误,比如NTP服务同步导致时间倒退。常见处理方式有:

  • 拒绝生成ID并报错;
  • 进入等待状态直到时间追上;
  • 添加机器ID冗余控制;
  • 记录最近时间戳并强制递增(可能造成重复风险);

六、简易实现思路(Java伪代码)

java 复制代码
long timestamp = currentTimeMillis();
if (timestamp == lastTimestamp) {
    sequence = (sequence + 1) & MAX_SEQUENCE;
    if (sequence == 0) {
        timestamp = waitNextMillis(lastTimestamp);
    }
} else {
    sequence = 0;
}
lastTimestamp = timestamp;

long id = ((timestamp - startTime) << timestampShift)
        | (datacenterId << datacenterShift)
        | (workerId << workerShift)
        | sequence;

七、延伸与改进

  • Leaf算法(美团) :通过数据库或Zookeeper生成分布式ID。
  • 百度uid-generator:优化时间回拨、支持批量缓存ID。
  • Sonyflake / Instagram的Snowflake改版:针对特定平台优化。

补充:

🔸 1. 什么是"时间回拨"?为什么会影响 Snowflake 算法?

✅ 正常情况下:

Snowflake 算法用的是当前系统时间戳 System.currentTimeMillis()(单位:毫秒),每次生成的 ID 都会把这个时间戳"编码"进高位。这样就可以保证:

  • ID 是单调递增的;
  • 同一毫秒内用序列号区分。

⚠️ "时间回拨"是什么?

"时间回拨"是指系统当前时间突然比之前变小了,这是一个系统层面的时间异常,常见原因有:

  • 系统使用了 NTP 自动校时,比如同步时钟服务器,可能把系统时间往前调;
  • 手动改了系统时间;
  • 虚拟机时间漂移问题等。

❗ 举个简单的例子:

你在 2025年8月5日 10:00:00.000 生成了一个 ID,记录的时间戳是:

ini 复制代码
timestamp = 1691210400000

过了一秒,你又想生成 ID,此时时间戳理应更大:

ini 复制代码
timestamp = 1691210401000

但如果发生了"时间回拨",你的系统时间被错误地调成了:

ini 复制代码
timestamp = 1691210399000

这时候,新生成的ID时间戳比上一个小!

结果:ID 变得无序,甚至可能和之前的ID重复(尤其是当机器ID和序列号也一样) ,就破坏了全局唯一性。

相关推荐
爱勇宝14 小时前
第 1 章:别把“需求文档”当成真正的需求
前端·后端·程序员
IT_陈寒19 小时前
闭包陷阱让我加了两天班,JavaScript你真行
前端·人工智能·后端
易协同低代码20 小时前
通达OA核心类库TD类深度解析
后端
Gopher_HBo20 小时前
Go语言学习笔记(十八)Gin处理Session
后端
谭光志21 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
她说..21 小时前
Java 默认值设置方式
java·开发语言·后端·springboot
foggyprojects21 小时前
从0开始,一句话启动AI DataAgent
后端·数据分析·ai编程
郡杰21 小时前
一些基础和问题解决
后端
陈随易21 小时前
前端项目部署只要30秒
前端·后端·程序员
YIAN21 小时前
从零手写文件读取 MCP 服务:一文吃透 Model Context Protocol 全链路通信原理
前端·后端·mcp