试题
考点

java语言-分布式相关-分布式ID(雪花算法等)

面5笔5

Snowflake(雪花算法)

前往“校大”小程序,刷题更快
最新校招难题刷题,快来进刷题群吧
解答

snowflake 中文的意思是雪花,所以常被称为雪花算法。SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。

设计原理

java中,每个数据类型存储所占的字节数不一样,雪花算法生成的数字定义为 long,所以就是8个字节,64位,范围为:-2的64次方 ~ 2的64次方减1,考虑到生成的唯一值用于数据库主键,所以理论值应该从正数开始,容量上也是能满足业务,所以第一位为0是正数,最终范围为:0~9223372036854775808(2的63次方减1)。

组成原理

1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0。

41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 得到的值,这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69。

10位的数据机器位,可以部署在1024个节点,包括5位 datacenterId 和5位 workerId 。

12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号。加起来刚好64位,为一个Long型。

上面总体是64位,具体位数可自行配置,如想运行更久,需要增加时间戳位数;如想支持更多节点,可增加工作机器id位数;如想支持更高并发,增加序列号位数

优点

整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。

Java代码

核心

1.线程安全

生成ID的方法是加了synchronized 关键词,确保了线程安全,否则在并发情况下,生成的Id就有可能重复。

同一毫秒生成多个Id时

根据雪花算法的组成,可以看出,如果同一台机器同一毫秒需要生成多个Id,因为毫秒的时间戳、数据机器位一样,则前52位一致,所以需要靠后12位的序列号来区分。lastTimestamp 记录了上一次生成Id的毫秒级的时间戳,timestamp 为当前生成Id时毫秒级的时间戳,如果同一毫秒生成多个id,要生成不同序列号,序列号 sequence 开始为0。

sequence 递增到 4095 要重新回到 0 ,则使用 tilNextMillis 方法阻塞到下一毫秒并赋值给 timestamp,不断获取当前时间和最近生成Id的时间戳进行判断,如果还在当前毫秒级别,则空转,直到下一毫秒。获取新的时间戳后,此时 sequence 回到0。

3.移位并通过或运算拼到一起组成64位的ID

优点: 雪花算法生成的ID是趋势递增,不依赖数据库等第三方系统,生成ID的效率非常高,稳定性好,可以根据自身业务特性分配bit位,比较灵活。

缺点: 每台机器的时钟不同,当时钟回拨可能会发生重复ID。当数据量大时,需要对ID取模分库分表,在跨毫秒时,序列号总是归0,会发生取模后分布不均衡。

如何解决时间回拨问题

时间回拨是指,当机器出现问题,时间可能回到之前,此时雪花算法生成的id可能与之前的id值相同,从而导致id重复。

1.系统抛出异常,运维来手动调整时间。

2.延迟等待,对于偶然性的时间回拨,也许是机器出现了一次小故障,频繁出现的概率并不大,所以对于这种情况没必要中断业务,可以采用阻塞线程5ms,再获取时间,对比看时间是否比上一次请求的时间大,如果大了,说明恢复正常了,则不用管;如果还小,说明真出问题了,则抛出异常,呼唤程序员处理。

3.备用机方式来解决,当前机器出现问题,迅速换一台机器,通过高可用解决。

评论
暂无评论

加载更多