Java系列之雪花算法和原理

2022-08-25 07:58:35

SnowFlake 算法:是 Twitter 开源的分布式 id 生成算法。
核心思想:使用一个 64 bit 的 long 型的数字作为全局唯一 id。
首先了解一下雪花ID的结构:从网上盗用一张;
在这里插入图片描述
针对上面各个部分做简单说明:

  1. 1bit:不用;因为二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所以最高位固定为0。

  2. 41bit-时间戳,用来记录时间戳,毫秒级。

  • 41位可以表示241-1个数字,
  • 如果只用来表示正整数(计算机中正数包含0),可以表示的数值范围是:0 至 241- 1;减1是因为可表示的数值范围是从0开始算的,而不是1。
  • 也就是说41位可以表示241-1个毫秒的值,转化成单位年则是(241-1) / (1000 * 60 * 60 * 24 * 365)=69年
  1. 10bit-工作机器id,用来记录工作机器id。代表的是这个服务最多可以部署在 210 台机器上,也就是 1024 台机器。
  • 可以部署在个节点,包括5位datacenterId和5位workerId;
  • 10 bit 里 5 个 bit 代表机房 id,5 个 bit 代表机器 id。意思就是最多代表 25 个机房(32 个机房),每个机房里可以代表 25 个机器(32 台机器),也可以根据自己公司的实际情况确定
  • 5位(bit)可以表示的最大正整数是,即可以用0、1、2、3、…31这32个数字,来表示不同的datecenterId或workerId;
  1. 12bit-序列号,序列号,用来记录同毫秒内产生的不同id。
  • 12位(bit)可以表示的最大正整数是212 - 1 ,即可以用0、1、2、3、…4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。

  • 对于分布式中雪花ID的应用理解:
    SnowFlake算法生成的ID大致上是按照时间递增的,用在分布式系统中时,需要注意数据中心标识和机器标识必须唯一,这样就能保证每个节点生成的ID都是唯一的。或许我们不一定都需要像上面那样使用5位作为数据中心标识,5位作为机器标识,可以根据我们业务的需要,灵活分配节点部分,如:若不需要数据中心,完全可以使用全部10位作为机器标识;若数据中心不多,也可以只使用3位作为数据中心,7位作为机器标识
    snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。据说:snowflake每秒能够产生26万个ID。

综上,简单举例说明一下:

0 - 0001100101 0001010111 1101000100 1010111000 0 - 10001 - 11001 - 000000000000

第一个部分,是 1 个 bit:0,这个是无意义的。
第二个部分是 41 个 bit:表示的是时间戳。
第三个部分是 5 个 bit:表示的是机房 id,10001。
第四个部分是 5 个 bit:表示的是机器 id,11001。
第五个部分是 12 个 bit:表示的序号,就是某个机房某台机器上这一毫秒内同时生成的 id 的序号,000000000000。

源码实现

package com.qxy.utils;/**
 * @author wx
 * */publicfinalclassSnowFlake{// 起始的时间戳privatefinalstaticlong START_STMP=1577808000000L;//2020-01-01// 每一部分占用的位数,就三个privatefinalstaticlong SEQUENCE_BIT=12;//序列号占用的位数privatefinalstaticlong MACHINE_BIT=5;//机器标识占用的位数privatefinalstaticlong DATACENTER_BIT=5;//数据中心占用的位数// 每一部分最大值privatefinalstaticlong MAX_DATACENTER_NUM=~(-1L<< DATACENTER_BIT);privatefinalstaticlong MAX_MACHINE_NUM=~(-1L<< MACHINE_BIT);privatefinalstaticlong MAX_SEQUENCE=~(-1L<< SEQUENCE_BIT);// 每一部分向左的位移privatefinalstaticlong MACHINE_LEFT= SEQUENCE_BIT;privatefinalstaticlong DATACENTER_LEFT= SEQUENCE_BIT+ MACHINE_BIT;privatefinalstaticlong TIMESTMP_LEFT= DATACENTER_LEFT+ DATACENTER_BIT;privatelong datacenterId;//数据中心privatelong machineId;//机器标识privatelong sequence=0L;//序列号privatelong lastStmp=-1L;//上一次时间戳publicSnowFlake(long datacenterId,long machineId){if(datacenterId> MAX_DATACENTER_NUM|| datacenterId<0){thrownewIllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");}if(machineId> MAX_MACHINE_NUM|| machineId<0){thrownewIllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");}this.datacenterId= datacenterId;this.machineId= machineId;}//产生下一个IDpublicsynchronizedlongnextId(){long currStmp=timeGen();if(currStmp< lastStmp){thrownewRuntimeException("Clock moved backwards.  Refusing to generate id");}if(currStmp== lastStmp){//if条件里表示当前调用和上一次调用落在了相同毫秒内,只能通过第三部分,序列号自增来判断为唯一,所以+1.
                sequence=(sequence+1)& MAX_SEQUENCE;//同一毫秒的序列数已经达到最大,只能等待下一个毫秒if(sequence==0L){
                    currStmp=getNextMill();}}else{//不同毫秒内,序列号置为0//执行到这个分支的前提是currTimestamp > lastTimestamp,说明本次调用跟上次调用对比,已经不再同一个毫秒内了,这个时候序号可以重新回置0了。
                sequence=0L;}

            lastStmp= currStmp;//就是用相对毫秒数、机器ID和自增序号拼接return(currStmp- START_STMP)<< TIMESTMP_LEFT//时间戳部分| datacenterId<< DATACENTER_LEFT//数据中心部分| machineId<< MACHINE_LEFT//机器标识部分| sequence;//序列号部分}privatelonggetNextMill(){long mill=timeGen();while(mill<= lastStmp){
                mill=timeGen();}return mill;}privatelongtimeGen(){return System.currentTimeMillis();}publicstaticvoidmain(String[] args){
        SnowFlake snowFlake=newSnowFlake(2,3);for(int i=0; i<1000; i++){
            System.out.println(snowFlake.nextId());}}}

总结:
SnowFlake 算法系统:首先从配置文件中读取自己所在的机房和机器,比如机房 id = 17,机器 id = 12。接着 SnowFlake 算法系统接收到这个请求之后,用二进制位运算的方式生成一个 64 bit 的 long 型 id,64 个 bit 中的第一个 bit 是无意义的。
接着 41 个 bit,就可以用当前时间戳(单位到毫秒),然后接着 5 个 bit 设置上这个机房 id,还有 5 个 bit 设置上机器 id。
最后再判断一下,当前这台机房的这台机器上这一毫秒内,这是第几个请求,给这次生成 id 的请求累加一个序号,作为最后的 12 个 bit。

  • 作者:溪~源
  • 原文链接:https://blog.csdn.net/xuan_lu/article/details/109861677
    更新时间:2022-08-25 07:58:35