雪花算法(Snowflake)是Twitter开源的一种分布式唯一ID生成算法,用于在分布式系统中生成全局唯一的ID。雪花算法生成的ID是一个64位的整数,通常表示为长整型(long)。
雪花算法的结构
雪花算法生成的ID由以下几部分组成:
- 符号位(1位):始终为0,保证生成的ID为正数。
- 时间戳(41位):记录生成ID的时间戳,精确到毫秒级。可以使用大约69年的时间。
- 机器ID(10位):标识生成ID的机器,可以支持1024台机器。
- 序列号(12位):同一毫秒内生成的多个ID的序列号,可以支持每毫秒生成4096个ID。
实现原理
-
时间戳:
- 使用当前时间戳减去一个固定的起始时间戳(通常是系统启动时间或一个固定的过去时间),得到一个相对时间戳。
- 41位的时间戳可以表示的最大值是
2^41 - 1
,大约是69年。
-
机器ID:
- 每个生成ID的机器都有一个唯一的机器ID,通常由数据中心ID和机器ID组成,共10位,可以支持1024台机器。
-
序列号:
- 在同一毫秒内,如果生成的ID数量超过4096个,则需要等待下一毫秒再生成新的ID。
- 序列号从0开始递增,每生成一个ID,序列号加1,直到达到4095,然后重置为0。
生成ID的步骤
-
获取当前时间戳:
- 获取当前系统时间戳(毫秒级),减去起始时间戳,得到相对时间戳。
-
检查时间戳是否发生变化:
- 如果当前时间戳与上一次生成ID的时间戳相同,则序列号加1。
- 如果序列号达到4095,则等待下一毫秒再生成新的ID。
-
组装ID:
- 将符号位、时间戳、机器ID和序列号按位拼接,生成最终的64位ID。
示例代码
以下是一个简单的雪花算法实现示例:
public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L; // 起始时间戳,例如Twitter的Snowflake起始时间
private final long workerIdBits = 10L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long timestampLeftShift = sequenceBits + workerIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(workerId << workerIdShift) |
sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1);
System.out.println(idGenerator.nextId());
}
}
总结
雪花算法通过组合时间戳、机器ID和序列号,生成全局唯一的64位ID。这种算法简单高效,适用于分布式系统中唯一ID的生成需求。通过合理配置机器ID和起始时间戳,可以满足不同规模和需求的系统。