本文目录导读:

我来详细讲解分布式ID雪花算法(Snowflake Algorithm)的原理和实现。
雪花算法简介
雪花算法是Twitter开源的分布式ID生成算法,用于在分布式系统中生成唯一的、有序的64位整数ID。
ID结构组成
1 bit | 41 bits | 10 bits | 12 bits
--------|-----------|-----------|-----------
符号位 | 时间戳 | 工作机器 | 序列号
(0) | (毫秒级) | ID | (自增)
各字段说明:
- 符号位(1bit):始终为0,保证ID为正数
- 时间戳(41bit):毫秒级时间戳,可使用69年
- 机器ID(10bit):支持1024台机器
- 序列号(12bit):同一毫秒内可生成4096个ID
核心代码实现
public class SnowflakeIdGenerator {
// 起始时间戳 (2021-01-01 00:00:00)
private final static long START_TIME = 1609459200000L;
// 各部分位数
private final static long WORKER_ID_BITS = 10L;
private final static long SEQUENCE_BITS = 12L;
// 最大值
private final static long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
// 左移位数
private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
private final static long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format(
"Worker ID must be between 0 and %d", MAX_WORKER_ID));
}
this.workerId = workerId;
}
// 生成下一个ID
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
// 时间回拨处理
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
// 同一毫秒内,序列号自增
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
// 序列号用完,等待下一毫秒
if (sequence == 0) {
timestamp = waitNextMillis(timestamp);
}
} else {
// 不同毫秒,序列号重置
sequence = 0L;
}
lastTimestamp = timestamp;
// 组合各部分生成ID
return ((timestamp - START_TIME) << TIMESTAMP_SHIFT)
| (workerId << WORKER_ID_SHIFT)
| sequence;
}
// 等待下一毫秒
private long waitNextMillis(long currentTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= currentTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
性能优化版本
public class OptimizedSnowflakeIdGenerator {
private static final long START_TIME = 1609459200000L;
private static final long BITS_SEQUENCE = 12;
private static final long BITS_MACHINE = 10;
private static final long MASK_SEQUENCE = ~(-1L << BITS_SEQUENCE);
private static final long MAGIC_NUMBER = 1 << (BITS_MACHINE + BITS_SEQUENCE);
private final long machineId;
private volatile long lastTimestamp = -1L;
private volatile long sequence = 0L;
// 预置好的机器ID部分
private final long machineIdPart;
public OptimizedSnowflakeIdGenerator(long machineId) {
this.machineId = machineId;
this.machineIdPart = machineId << BITS_SEQUENCE;
}
public long nextId() {
long timestamp = System.currentTimeMillis();
// 处理时钟回拨
if (timestamp < lastTimestamp) {
long offset = lastTimestamp - timestamp;
if (offset <= 5) {
// 小范围回拨,等待
timestamp = waitUntil(timestamp);
} else {
throw new IllegalStateException("Clock moved backwards");
}
}
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & MASK_SEQUENCE;
if (sequence == 0) {
timestamp = waitUntil(timestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
// 快速生成ID
return ((timestamp - START_TIME) * MAGIC_NUMBER)
+ machineIdPart
+ sequence;
}
private long waitUntil(long currentTimestamp) {
long timestamp = System.currentTimeMillis();
while (timestamp <= currentTimestamp) {
timestamp = System.currentTimeMillis();
}
return timestamp;
}
}
应用示例
public class SnowflakeDemo {
public static void main(String[] args) {
// 创建生成器(机器ID范围0-1023)
SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1);
// 批量生成ID
for (int i = 0; i < 10; i++) {
long id = generator.nextId();
System.out.println("Generated ID: " + id);
}
// 测试性能
long startTime = System.currentTimeMillis();
int count = 1000000;
for (int i = 0; i < count; i++) {
generator.nextId();
}
long endTime = System.currentTimeMillis();
System.out.println("生成 " + count + " 个ID耗时: "
+ (endTime - startTime) + "ms");
System.out.println("每秒生成: " + (count * 1000 / (endTime - startTime)) + " 个");
}
}
优缺点分析
✅ 优点
- 全局唯一:分布式环境下保证ID唯一性
- 趋势递增:整体呈递增趋势,有利于数据库索引
- 高性能:纯内存计算,单机百万级/秒
- 业务含义:可反解析出生成时间、机器等信息
❌ 缺点
- 依赖机器时钟:时钟回拨可能导致ID重复
- 有限容量:单机每秒最多生成409.6万个(4096*1000)
改进方案
1 时钟回拨处理
// 方案1:等待时钟追上
private long handleClockBackwards(long lastTimestamp) {
long currentTimestamp = System.currentTimeMillis();
while (currentTimestamp < lastTimestamp) {
currentTimestamp = System.currentTimeMillis();
}
return currentTimestamp;
}
// 方案2:使用备用序列号
private long handleClockBackwardsWithBackup(long lastTimestamp, long sequence) {
if (sequence > 0) {
// 使用前一毫秒的序列号继续生成
this.sequence = (sequence + 1) & MAX_SEQUENCE;
return lastTimestamp;
}
// 等待时钟追上
return waitForClock(lastTimestamp);
}
2 扩展版本(百亿级ID)
public class ExtendedSnowflakeIdGenerator {
// 增加时间戳位数到51位
private static final long TIMESTAMP_BITS = 51L;
// 减少机器位和序列号位
private static final long WORKER_ID_BITS = 8L;
private static final long SEQUENCE_BITS = 4L;
// ... 实现逻辑类似,但支持更长时间
}
最佳实践
- 机器ID管理:通过Zookeeper或Redis统一分配
- 时钟同步:使用NTP服务同步时钟,避免大幅回拨
- 高可用部署:多个ID生成节点,通过负载均衡使用
雪花算法是分布式系统中ID生成的首选方案,理解其原理对系统设计很重要。