雪花算法(SnowFlake)
组成结构图
代码实现(基于上图)
public class SnowFlakeGenerator {
private long roomId; //机房id
private long workerId; //机器id
private long roomIdBit = 5l; //占用5个bit位
private long workerIdBit = 5l;//占用5个bit位
//声明roomId最大的正整数 32
private long maxRoomId = -1l ^ (-1l << roomIdBit);
private long maxWorkerId = -1l ^ (-1l << workerIdBit);
private long sequenceBits = 12l; //12bit的递增序列
private long sequence; //递增开始的序列
public SnowFlakeGenerator(long roomId, long workerId, long sequence) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id错误");
}
if (roomId > maxRoomId || roomId < 0) {
throw new IllegalArgumentException("roomId Id错误");
}
this.roomId = roomId;
this.workerId = workerId;
this.sequence = sequence;
}
private long sequenceMask = -1l ^ (-1l << sequenceBits);
private long lastTimeStamp = -1l; //存储上一次生成的id的时间戳
private long twepoch = 1596372483166L;//初始的时间值
private long workerIdShift = sequenceBits;
private long roomIdShift = sequenceBits + workerIdBit;
private long timeStampShift = sequenceBits + workerIdBit + roomIdBit;
public synchronized long nextVal() {
long timestamp = System.currentTimeMillis();
if (timestamp < lastTimeStamp) {
throw new RuntimeException("时间戳异常");
}
if (lastTimeStamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = waitToNextMills(lastTimeStamp);
}
} else {
//如果进入到了新的时间毫秒,sequence从0开始
sequence = timestamp & 1;
}
lastTimeStamp = timestamp;
return ((timestamp - twepoch) << timeStampShift | (roomId << roomIdShift) | (workerId << workerIdShift) | sequence);
}
public long waitToNextMills(long lastTimeStamp) {
long timeStamp = System.currentTimeMillis();
while (timeStamp <= lastTimeStamp) {
timeStamp = System.currentTimeMillis();
}
return timeStamp;
}
public static void main(String[] args) throws InterruptedException {
SnowFlakeGenerator snowFlakeGenerator = new SnowFlakeGenerator(1,1,1);
for (int i = 0; i < 100; i++) {
Thread.sleep(1);
System.out.println(snowFlakeGenerator.nextVal());
}
}
}
这种方式的优缺点是:
优点:
- 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
- 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
- 可以根据自身业务特性分配bit位,非常灵活。
缺点:
- 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。