分布式雪花算法
简介
在分布式系统中生成全局唯一的ID是一个很常见的需求。雪花算法(Snowflake)是Twitter开源的一个生成全局唯一ID的算法,能够满足分布式系统中生成全局唯一ID的需求。
雪花算法的核心思想是通过利用64位的二进制数据,将其分为不同的部分来生成全局唯一ID。具体来说,雪花算法生成的ID包含一个 41 位的时间戳(毫秒级),10 位的机器ID 和 12 位的序列号。通过这种方式,即使在同一毫秒内,也能保证生成的ID全局唯一。
算法原理
64位二进制结构
雪花算法生成的ID是一个64位的二进制数据,将其分为不同的部分:
- 第一个部分是 41 位的时间戳,用来表示生成ID的时间戳(毫秒级)。
- 第二部分是 10 位的机器ID,用来表示机器的唯一标识。
- 第三部分是 12 位的序列号,用来表示同一时间戳内生成的序列号。
生成ID的规则
具体生成ID的规则如下:
- 时间戳部分:41 位的时间戳,从系统当前时间开始计算,可以支持 69 年的使用。
- 机器ID部分:10 位的机器ID,可以通过配置来指定每台机器的唯一标识。
- 序列号部分:12 位的序列号,在同一时间戳内生成多个ID时递增。
代码示例
下面是一个使用Java语言实现的分布式雪花算法的示例代码:
/**
* 雪花算法生成全局唯一ID
*/
public class SnowflakeIdGenerator {
private final long twepoch = 1288834974657L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeIdGenerator(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("Worker ID can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("Datacenter ID can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id for " + (lastTimestamp - timestamp) + " milliseconds");
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(datacenterId << datacenterIdShift) |
(workerId << workerIdShift) |
sequence;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
SnowflakeIdGenerator idWorker = new SnowflakeIdGenerator(1, 1);
for (int i = 0; i < 10; i++) {
long id