一、概述
雪花算法id是一个分布式可用的id生成器,也是现在广泛应用的分布式情况下生成全局唯一id的解决方案之一,所以对雪花算法id的深入了解十分有必要。
二、原理
雪花算法id的原理是利用当前时间戳、工作机器id、序列号三个部分来生成全局唯一id。
1、时间戳
时间戳是雪花算法id生成的第一部分,一般为41位,能够使用的时长最长约69年,当前时间戳减去一个固定的时间点比如“2020-01-01”后右移22位即可获得一个41位的时间戳。
2、工作机器id
工作机器id是一个在分布式系统中必须唯一的标识,一般为10位,5位表示数据中心id,5位表示机器id,能够使用的最多数据中心id数量是32个,每个数据中心内部最多机器数量是32个。
3、序列号
序列号是每个工作机器id内部单调递增的计数器,一般为12位,最多可以生成4096个id。序列号溢出后需要等到下一个毫秒进行生成。
三、代码实现
public class SnowflakeIdGenerator {
// 开始时间戳
private static final long BEGIN_TIMESTAMP = 1577808000000L;
// 数据中心id的位数
private static final long DATA_CENTER_ID_BITS = 5L;
// 机器id的位数
private static final long MACHINE_ID_BITS = 5L;
// 序列号的位数
private static final long SEQUENCE_BITS = 12L;
// 数据中心id最大值
private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
// 机器id最大值
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
// 序列号最大值
private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);
// 数据中心id左移位数(机器id内部共享此位)
private static final long DATA_CENTER_LEFT_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;
// 机器id左移位数
private static final long MACHINE_LEFT_SHIFT = SEQUENCE_BITS;
// 时间戳左移位数
private static final long TIMESTAMP_LEFT_SHIFT = DATA_CENTER_LEFT_SHIFT + DATA_CENTER_ID_BITS;
// 数据中心id
private long dataCenterId;
// 机器id
private long machineId;
// 序列号
private long sequence;
// 最后生成id的时间戳
private long lastTimestamp = -1L;
/**
* 构造函数
* @param dataCenterId 数据中心id
* @param machineId 机器id
*/
public SnowflakeIdGenerator(long dataCenterId, long machineId) {
if (dataCenterId < 0 || dataCenterId > MAX_DATA_CENTER_ID) {
throw new IllegalArgumentException("data center id can't be greater than " + MAX_DATA_CENTER_ID + " or less than 0");
}
if (machineId < 0 || machineId > MAX_MACHINE_ID) {
throw new IllegalArgumentException("machine id can't be greater than " + MAX_MACHINE_ID + " or less than 0");
}
this.dataCenterId = dataCenterId;
this.machineId = machineId;
}
/**
* 生成id方法
* @return id
*/
public synchronized long generateId() {
long currentTimestamp = System.currentTimeMillis();
if (currentTimestamp < lastTimestamp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & MAX_SEQUENCE;
if (sequence == 0) {
currentTimestamp = waitUntilNextTime(lastTimestamp);
}
} else {
sequence = 0;
}
lastTimestamp = currentTimestamp;
return ((currentTimestamp - BEGIN_TIMESTAMP) << TIMESTAMP_LEFT_SHIFT)
| (dataCenterId << DATA_CENTER_LEFT_SHIFT)
| (machineId << MACHINE_LEFT_SHIFT)
| sequence;
}
/**
* 等待下一毫秒
* @param lastTimestamp 最后生成id的时间戳
* @return 下一毫秒的时间戳
*/
private long waitUntilNextTime(long lastTimestamp) {
long time = System.currentTimeMillis();
while (time <= lastTimestamp) {
time = System.currentTimeMillis();
}
return time;
}
}
四、使用场景
雪花算法id适用于需要生成全局唯一id的场景,比如订单号、用户id等。在分布式场景下,也可以用于解决单个节点出现的并发问题。
五、总结
通过对雪花算法id的深入介绍,相信大家对它有了更深入的了解。在开发过程中,如果需要生成全局唯一id,可以考虑使用雪花算法id。