雪花算法(Snowflake)
1.雪花算法的介绍分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成,那么这个时候我们可以考虑一下雪花算法。特别说明:1个bit是一个值0或1叫做一个二进制位,而1个byte占用8个bit,这是byte与bit的区别。雪花算法所产生的唯一ID是由64个二进制位转换为十进制位以后所对应的值第一个部分是1个bit,固定值为0,这个
1.雪花算法的介绍
分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成,那么这个时候我们可以考虑一下雪花算法。

特别说明:1个bit是一个值0或1叫做一个二进制位,而1个byte占用8个bit,这是byte与bit的区别。
雪花算法所产生的唯一ID是由64个二进制位转换为十进制位以后所对应的值
第一个部分是1个bit,固定值为0,这个是无意义的
第二个部分是41个bit:表示的是时间戳
第三个部分是5个bit:表示的是机房id,例如“10001”
第四个部分是5个bit:表示的是机器id,例如“11001”
第五个部分是12个bit:表示的是序号,就是某个机房某台机器上这一毫秒内同时生成的id的序号,例如“0000000000”
第一个部分的1bit是不用的,为啥呢?
因为二进制里第一个bit为如果是1,那么都是负数,但是我们生成的id都是正数,所以第一个bit统一都是0。
第二个部分的41bit表示的时间戳,单位是毫秒
41 bit 可以表示的数字多达2^41-1,也就是可以标识2^41-1个毫秒值,换算成年就是表示69年的时间。年T=(1L<<41)/(1.00OL* 60* 60* 24* 365)= 69,其中1L<<41表示的是位运算,速度比2^41-1这种速度更快一点。
第三、四个部分的10bit记录工作机器id,代表的是这个服务最多可以部署在2^10台机器上,也就是1024台机器
但是10bit里5个bit 代表机房id,5个 bit代表机器id。意思就是最多代表2^5个机房(32个机房),每个机房里可以代表2^5个机器(32台机器),也可以根据自己公司的实际情况确定。
第五个部分的12bit是用来记录同一毫秒内产生的不同id
12bit可以代表的最大正整数是2^12-1=4095,也就是说可以用这个12bit代表的数字来区分同一个毫秒内的4096个不同的 id。
2.雪花算法的源码
雪花算法(Snowflake)是由Twitter开源出来的,在Hutool依赖包中已经对该算法进行了实现
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.11</version>
</dependency>
我们在项目中使用该算法时,只需要pom.xml中引入上述依赖,然后可以考虑构造一个如下所示的唯一ID生成器封装类,当然也可以直接使用IdUtil.getSnowflake()方法。
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 雪花算法唯一ID生成器
*/
@Component
@Slf4j
public class IdGeneratorSnowflake {
// workerId表示机房编号,其取值范围为[0-31]
private final long workerId=1;
// datacenterId表示机房内某一个服务器的编号,其取值范围为[0-31]
private final long datacenterId =31;
private Snowflake snowflake;
@PostConstruct
public void init(){
snowflake=IdUtil.getSnowflake(workerId,datacenterId);
}
public synchronized long snowflakeId(){
return snowflake.nextId();
}
}
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class WorkApplicationTests {
@Autowired
private IdGeneratorSnowflake idGeneratorSnowflake;
@Test
void produceTest() {
for (int i = 0; i < 10; i++) {
System.out.println(idGeneratorSnowflake.snowflakeId());
}
}
}
执行上述测试类,其输出结果为:
1525670872306552832
1525670872306552833
1525670872306552834
1525670872306552835
1525670872306552836
雪花算法获取唯一ID主要是依靠snowflake.nextId()方法,其核心源码部分如下所示:
public synchronized long nextId() {
// 1.获取当前时间的时间戳
long timestamp = genTime();
// 2.判断当前时间戳是不是小于上一次的时间戳
if (timestamp < this.lastTimestamp) {
// 3.判断上一次的时间戳与当前时间戳是不是在可容忍阈值内
if(this.lastTimestamp - timestamp < timeOffset){
// 4.容忍指定的回拨,避免NTP校时造成的异常
timestamp = lastTimestamp;
} else{
// 5.如果服务器时间有问题(时钟后退) 报错。
throw new IllegalStateException(StrUtil.format("Clock moved backwards. Refusing to generate id for {}ms", lastTimestamp - timestamp));
}
}
if (timestamp == this.lastTimestamp) {
// 1.如果当前时间戳等于上一次的时间戳,则说明是在同一毫秒内去生成唯一ID,则序列号进行递增
// this.sequence的最大值为4095,this.sequence + 1以后值为4096,然后与SEQUENCE_MASK进行与运算所产生的值为0
final long sequence = (this.sequence + 1) & SEQUENCE_MASK;
// 2.判断同一毫秒内的序列数已经达到最大值
if (sequence == 0) {
// 3.循环等待下一个毫秒时间,获取下一个毫秒时间戳并赋值给当前时间戳
timestamp = tilNextMillis(lastTimestamp);
}
// 4.更新当前对象的序列号
this.sequence = sequence;
} else {
// 5.不同毫秒内,则序列号为0
this.sequence = 0L;
}
// 6.把当前时间戳更新到当前对象的lastTimestamp
// 即当前时间戳存档记录,用于下次产生id时对比是否为相同时间戳
this.lastTimestamp = timestamp;
return ((timestamp - twepoch) << TIMESTAMP_LEFT_SHIFT) // 时间戳部分
| (dataCenterId << DATA_CENTER_ID_SHIFT) // 数据中心部分
| (workerId << WORKER_ID_SHIFT) // 机器标识部分
| sequence; // 序列号部分
}
雪花算法生成唯一ID核心源码的总结:
1.当前时间戳的处理
2.当前所生成的序列号的处理
3.数据值部分进行左移操作,返回唯一ID
timestamp - twepoch:表示的是当前时间戳减去开始时间戳,这个开始的时间戳一定要是一个固定值,不能是一个可变的值。TIMESTAMP_LEFT_SHIFT表示时间差值向左移的位数,固定为22。
可以看到最后计算出来的3个数据值部分(除了序列号)都进行左移操作,这主要是为了实现雪花算法所生成的唯一ID是由64位二进制所组成的这么一种结构。
3.时钟回拨问题
其实这个问题,对于Hutool这个工具类,它其实已经做了一个优化,如果回拨差是在可容忍范围以内,则将当前时间时间戳更新后上一次记录的时间戳,这个主要是为了避免NTP校时所造成的影响;否则的话才会抛出一个异常。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)