雪花算法原理分析

为什么需要分布式ID?分布式ID的可选方案,优缺点?
基础数据类型:
int类型最大值10亿级别(数量级,明显不够用)
float和double,存在不精准运算,有效位数并不大,占用字节最大同long
综合考量:long类型数据规很大,足够应用在ID策略上了
负数如何存储
位运算:&。如果两个对应的二进制位上的数都是1,结果是1,其他都是0
按位或:|。如果两个对应的二进制位上的数都是0,结果是0,其他都是1
按位异或:^。如果两个对应的二进制位上的数字相同,则运算结果为0,其他都是1
左移:<<。把二进制数据在内存空间中向左边移动。左移n相当于乘以2的n次方,但要注意两
点:1-左移带有符号位的,说明每个数据类型左移都有位数限制;2-左移后原来的值不变,移位
后是一个新的值
补码:该数的原码除符号位外各位取反,然后在最后一位加1
-1L去除符号位原码:
取反:
加1:
原理分析
前置条件:单机,机器中心:31机器号吗:31
workID:可认为是机器特征号码,一般由两部分组成:数据中心+机器号,各占用5位。也就是最
大值2^5*2^5=2^10=1024个值(0-1023)
单机情况下,如果不考虑时钟回拨,上图中,workID对于单机固定,12位序列化的变化范围为:
2^12=4096个值(0-4095)
这么分析,单机该种算法,每一毫秒可产生4096个可用不重复的序列号
1s内4096*1000=4096000个。足够很多场景下使用了。
时间部分:
69年都不会重复,既然只能使用69年,我们系统中的时间,是从1970年开始的,所以设计上,设
置一个起始时间,也就是项目开始的时间,目的为了使用更久的时间
代码:
publicsynchronized long nextId (){
  long timestamp = timeGen ();
  if ( timestamp < lastTimestamp ){
    System . err . printf ( "clockismovingbackwards.Rejectingrequestsuntil%d." ,
lastTimestamp );
   thrownewRuntimeException ( String .format ( "Clockmovedbackwards.Refusingtogenerate
idfor%dmilliseconds" ,
        lastTimestamp - timestamp ));
  if ( lastTimestamp == timestamp ){
    sequence = ( sequence + 1 ) & sequenceMask ;
    if ( sequence == 0 ){
      timestamp = tilNextMillis ( lastTimestamp );
  }
  
} else {
    sequence = 0 ;
}
  lastTimestamp = timestamp ;
  return (( timestamp - twepoch ) << timestampLeftShift ) |
    ( datacenterId << datacenterIdShift ) |
    ( workerId << workerIdShift ) |    sequence ;
}
if (timestamp < lastTimestamp) { //lastTimestamp,上次生成时间,本次生成时间
timestamp ,如果小于,说明时钟回拨
if (lastTimestamp == timestamp) {//说明两次生成ID的时间戳在同一毫秒内,否则,每一s从0
开始生成最后的序列号。
最终如何组成结果:
return ((timestamp - twepoch) << timestampLeftShift) |
(datacenterId << datacenterIdShift) |
(workerId << workerIdShift) |
sequence;
每一个号段向左移位置,在按位或,如下图:
这里解释了最终结果的构成,非常巧妙,综合了性能,变化点,生成了非常完美,不容易重合的一
个long数字,那么问题只剩下,相同时间如何拿到后边的12位序列了
if(lastTimestamp==timestamp){
//如果等走到这里:说明来给你个问题,第一:肯定生成过一次序列,那么sequence一定不可能从0开始
//sequenceMark=-1L^(-1L<<sequenceBits)经过前面基础知识,可知
//sequenceMask=0000000000000000000000000000000000000000000000000000111111111111
//如果sequence=0,说明:
//sequence+1=0000000000000000000000000000000000000000000000000001000000000000
//sequence= 0000000000000000000000000000000000000000000000000000111111111111
//说明此时sequence=4095,及已经增长到最大值
//初始进入此条件的时候,sequence=
0000000000000000000000000000000000000000000000000000000000000000
//经过sequence=(sequence+1)&sequenceMask;
//sequence=0000000000000000000000000000000000000000000000000000000000000001
sequence=(sequence+1)&sequenceMask;
//如果此处等于零,相当于4095之后,在同一秒内再次需要生成序列,此时根据设计12位,已经不能在生成
了,所以,相当于系统调整了下时间,把当前时间修改到下一秒钟,参考tilNextMillis(lastTimestamp),不难理解了。
if(sequence==0){timestamp=tilNextMillis(lastTimestamp);
}
}
存在的问题:
问题1:
雪花算法的设计等价于单台机器
不变(符号位置)+变化(时间戳)+不变(最大31)+不变(最大31)+变化(0-+4096)
多台机器
不变(符号位置)(不变)+变化(时间戳)(多机会重复)+(不变(最大31)()+不变(最大31))
(组合不变)+变化(0-+4096)(可能重复)
等价于:
不变+不变+(变化)(自由组合)+不变
自由组合:可以分成2段,也可以是云主机一段,每个IP不通即可
问题2:时间回拨
这种设计,严重依赖服务器的时间,但是时间,不仅是哲学家的难题,也是计算机领域的一大难
题,至少linux上,存在时间同步等产生的时间跳跃问题,在分布式环境中,如果事件发生回拨,
则很大概率产生重复的ID。
  • 46
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值