雪花算法(Snowflake algorithm)是由 Twitter 开发的一种用于生成全局唯一ID的算法。它的设计目标主要是在分布式系统中提供一个唯一的ID生成方案,每个ID都是全局唯一的、趋势递增、64位的整数。这种算法是分布式系统中常用的ID生成方案之一。
雪花算法生成的ID是一个64位的整数,其中各个部分的组成如下:
- 符号位(最高位):一般不用。
- 时间戳部分(41位):用来表示生成ID的时间戳(毫秒级),可以支持长达69年的使用。
- 机器ID部分(10位):用来表示不同的机器,可以表示1024个节点。
- 序列号部分(12位):同一毫秒内可以生成的不同ID序列号,最多可以产生4096个不同的ID。
以下是一个雪花算法的详解:
-
时间戳部分:由于时间戳部分占用了41位,可以支持长达69年的时间范围,通常从某个固定的时间点(如格林尼治时间1970年1月1日00:00:00)开始计时。
-
机器ID部分:10位的机器ID可以支持 1024 个不同的节点。在分布式系统中,每个节点分配一个唯一的机器ID,用来标识不同的机器。这样每个节点都能保证生成的ID是唯一的。
-
序列号部分:每毫秒内可以生成的不同序列号最多为 4096 个。当在同一毫秒内产生多个ID时,会自动递增序列号,并确保每个ID的唯一性。
雪花算法的唯一性来自于对时间戳、机器ID和序列号的合理运用。值得注意的是,雪花算法生成的ID是趋势递增的,不同节点上生成的ID没有绝对的顺序性。
总的来说,雪花算法通过时间戳、机器ID和序列号的合理组合,使得生成的ID具有全局唯一性、趋势递增性、分布式、高并发的特点。这使得雪花算法成为分布式系统中常用的ID生成方案。
以下是一个 Java 实现的雪花算法示例:
public class SnowflakeIdWorker {
private final long workerId;
private final long epoch = 1420041600000L; // 基准时间戳
private long sequence = 0L;
private final long workerIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
public SnowflakeIdWorker(long workerId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id can't be greater than " + maxWorkerId + " or less than 0");
}
this.workerId = workerId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < 0) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (timestamp != timeGen()) {
sequence = 0L;
} else {
sequence = (sequence + 1) & 0x1f; // 十进制31,11111
// 同一毫秒内的序列号已经达到最大值,等待下一毫秒重新生成
if (sequence == 0) {
timestamp = tilNextMillis(timestamp);
}
}
return ((timestamp - epoch) << 22) | (workerId << 17) | sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
上面是一个简单的Snowflake算法的Java实现。在真实场景中,可能还要考虑时钟回拨的问题、并发的处理能力等。