雪花算法 详解

雪花算法

1.是什么

(SnowFlake)由Twitter创造的,是一个唯一ID生成算法,且具备有序性和可扩展性.

2.怎么组成

生成出的ID是一个64bits的整数,其中时间戳占41位,数据中心ID占5位,机器ID占5位,自增序列占12位,还有1位是符号位。

这个符号位可以是无符号,这样就可以扩大使用时间或其他地方的bit位数,但在JAVA中无法忽略符号位,因为JAVA中没有unsigned这么个玩意。

首位废弃41位时间戳5位机房码5位机器码12位序列号
000000011 00001100 01010000 11001010 00110000 0111111111100000000 0000
#0b11111110101111100001010000100000000100010000000010000000000001
#可以看到上文所述的第一位是标识符,此后是41位的时间戳,紧接着10位的节点标识码,最后12位的递增序列,从后面数12位是:000000000001,再数5位是:00010  这5位就是某个节点的存储标识,但是它目前是二进制,我们再将它转换为十进制

3.组成部位的作用

  1. 41位时间戳,用来根据时间变化做递增,并且可以和最后一次生成时间做对比,如果处于同一毫秒。该如何处理
  2. 10位数据中心ID,相同的代码,相同的时间,但数据中心ID不同,就不会生成出相同的UUID。
    机器ID,相同的代码,相同的时间,相同数据中心,但机器ID不同,就不会生成出相同的UUID。
  3. 12位递增序列,如果处于同一毫秒内,递增序列则可以自增,保证ID的不唯一,自增ID最大12字节也就是2^12-1(4095),也就是说,一台机器,可以在同1毫秒内生成4096个ID(为0时也算一个),一秒就可以生成4,096,000‬个ID。

4.解决了什么问题

首先要知道使用数据库自增ID有什么问题?

  1. 如果数据库使用AUTO_INCREMENT配合replace into自增ID,那么每当数据插入都会占用自增锁和插入锁
  2. 尝试爆破就可以大概了解此公司业务量
  3. 使用redis String进行increment生成有序UUID,redis如果为了高可用,你还要设置redis集群中的服务器对应的自增序列步长。
  4. UUID库生成字符串唯一ID,没有有序性,查找数据性能较差(在数据库中的索引如果没有有序性,插入时索引就要不停的调平衡,哪怕是B+Tree也扛不住你随机字符串的UUID)。
  5. 单机情况下有些人用时间戳+几位随机数做ID,分布式肯定凉凉,并且单机情况下也会有几率重复。你无法确定同一时间内是否会出现相同的随机数。

5.使用流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qqUdHSMh-1655642945343)(C:\Users\guxingyu\AppData\Roaming\Typora\typora-user-images\image-20220616220611617.png)]

时钟回拨

因为机器的原因会发生时间回拨,我们的雪花算法是强依赖我们的时间的,如果时间发生回拨,有可能会生成重复的ID,在我们上面的nextId中我们用当前时间和上一次的时间进行判断,如果当前时间小于上一次的时间那么肯定是发生了回拨,算法会直接抛出异常

**注:**这里简单说一下时钟回拨的解决思路:

第一种: 发现时钟回拨可以进行上锁等待差值后重试。

第二种: 借用未来时间
发现时钟回拨后利用redis或本地文件映射至内存存储差值,进行增加currentTimeMillis变量的值。如果一段时间内没有申请ID,那么currentTimeMillis就会变得>lastTimeStamp,此时清除差值即可。

第三种:从架构上解决

雪花服务分为两部分,每部分N台机器,如果每部分服务中有⼀台机器发⽣了时间回拨,那么就负载均衡访问另⼀部分的服务。如果另⼀部分服务还是时间回拨,那么才报错。

参考链接:https://blog.csdn.net/slslslyxz/article/details/106330758

6.使用方法

#首先安装库
pip3 install pysnowflake
#安装完成后,就可以在本地命令行启动snowflake服务
snowflake_start_server --worker=1

#生成唯一id
import snowflake.client
print(snowflake.client.get_guid())
#4589032814791368705
#解析成二进制
print(bin(4589032814791368705))
#0b11111110101111100001010000100000000100010000000010000000000001
#可以看到上文所述的第一位是标识符,此后是41位的时间戳,紧接着10位的节点标识码,最后12位的递增序列,从后面数12位是:000000000001,再数5位是:00010  这5位就是某个节点的存储标识,但是它目前是二进制,我们再将它转换为十进制
print(int('00010',2))

7.使用过程中可能出现的问题

原因:,由于每毫秒从0开始计数,如果id生成不频繁,即每毫秒只会生成一个甚至几毫秒才需要生成一个,那么计数器就永远都是0,生成的id用于取模分表的话,就会出现永远都是插入到一个表(或规律的那几个表),无法达到均匀分布在各表的目的,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值