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和序列号也一样) ,就破坏了全局唯一性。

相关推荐
有来技术7 小时前
Spring Boot 4 + Vue3 企业级多租户 SaaS:从共享 Schema 架构到商业化套餐设计
java·vue.js·spring boot·后端
东东5168 小时前
学院个人信息管理系统 (springboot+vue)
vue.js·spring boot·后端·个人开发·毕设
三水不滴8 小时前
Redis缓存更新策略
数据库·经验分享·redis·笔记·后端·缓存
小邓吖9 小时前
自己做了一个工具网站
前端·分布式·后端·中间件·架构·golang
大爱编程♡9 小时前
SpringBoot统一功能处理
java·spring boot·后端
好好研究12 小时前
总结SSM设置欢迎页的方式
xml·java·后端·mvc
小马爱打代码12 小时前
Spring Boot:第三方 API 调用的企业级容错设计
java·spring boot·后端
csdn2015_14 小时前
springboot task
java·spring boot·后端
czlczl2002092514 小时前
Spring Boot :如何高性能地在 Filter 中获取响应体(Response Body)
java·spring boot·后端
码界奇点14 小时前
基于Spring Boot和Vue3的无头内容管理系统设计与实现
java·spring boot·后端·vue·毕业设计·源代码管理