雪花算法原理(设计原理、优缺点、如何改造它、以及应用)
- 雪花算法源码
- 为什么雪花算法是 64 位?
- 为什么时间戳是41位?占雪花算法的 43-47 bit 位
- 为什么工作台最大只支持设置 31 ?
- 工作台设置成了 63 会导致什么后果?
- 同机器位只支持最大 5 bit位
- 同理数据累加位只支持最大 12 bit位
- 雪花算法优点
- 雪花算法缺点
- 雪花算法改造
- 小咸鱼的技术窝
雪花算法源码
雪花算法是一个开源的分布式生成唯一自增 Id 的这么一个工具类。主要的源码如下
/*** 常规雪花算法:1-42bit位:时间戳 (共 42 位)* 43-47 bit位:数据中心ID (共 5 位,最大支持 2 的 5 次方减 1 个数据中心)* 48-52 bit位:工作台ID (共 5 位,最大支持 2 的 5 次方减 1 个工作台)* 53-64 bit位:累加数ID (共 12 位,最大支持 2 的 12 次方)*/
public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;return ((timestamp - twepoch) << 22)| (datacenterId << 17)| (workerId << 12)| sequence;
}
都知道雪花算法结构如下,不知道读者有没有想过 为什么雪花算法是 64 位的?为什么工作机器最大为 5 位?,反正我第一次接触雪花算法的时候,就想过这些问题。
- 1-42bit位:时间戳 (共 42 位)
- 43-47 bit位:数据中心ID (共 5 位,最大支持 2 的 5 次方减 1 个数据中心)
- 48-52 bit位:工作台ID (共 5 位,最大支持 2 的 5 次方减 1 个工作台)
- 53-64 bit位:累加数ID (共 12 位,最大支持 2 的 12 次方)
为什么雪花算法是 64 位?
因为雪花算法返回的是一个 long 类型的值,换算成二进制就是最大 64 位!
为什么时间戳是41位?占雪花算法的 43-47 bit 位
随便一个时间戳转换成二进制就是 41 位
log.info("时间戳:{} 位", Long.toBinaryString(System.currentTimeMillis()).length());
且雪花算法中的时间戳左移了 22 位,加上时间戳本身的 41 位,加起来就有 63 位了,加上第一位的符号位,就是 64 位,正好等于 long 类型的 64 位。完美利用 long 类型。
为什么工作台最大只支持设置 31 ?
时间戳达到了最大值,41 位时间戳 bit 位全是 1,留了后 22 位全是 0,由于或运算,运算位有 1 就是 1,所以说这个 22 位算是留给累加数、工作台、数据中心的有效左移位,超过这个有效左移位,就会出现生成重复 id 的情况出现。
111111111111111111111111111111111111111110000000000000000000000
工作台达到最大值 31(31换二进制为11111),左移22位后,会得到如下一个二进制数(位数不足63位,前面用0补齐)
000000000000000000000000000000000000000001111100000000000000000
最终得到的运算结果就是这个
111111111111111111111111111111111111111111111100000000000000000
工作台设置成了 63 会导致什么后果?
63对应的二进制是 6个1(111111),参与的运算结果和工作台设置成31得到的结果一样,就有产生的重复 id 的风险了。因此这也是工作台最大只支持 5 bit 位的原因
同机器位只支持最大 5 bit位
在时间戳、工作台按最大值运算后,留给数据中心的有效运算位只有 17 位了,然后数据中心又左移了12 位,留给数据中心的参与运算的bit就只有5位了(17-12=5)。同理如果数据中心设置成63,也会有生成重复id的风险出现
同理数据累加位只支持最大 12 bit位
在时间戳、机器位、工作台按最大值左移后,留给累加数的有效位只有12bit位了(63-41-5-5=12),看我下图圈绿的地方这个就是有效bit位。
雪花算法优点
按照官方来说就是:
不需要搭建服务集群,代码逻辑非常简单,同一毫秒内,订单ID的序列号自增。同步锁只作用于本机,机器之间互不影响,每毫秒可以生成4百万个订单ID
对比Mysql自增主键:
一般雪花算法用于生成订单ID,相比于 Mysql 自增主键来说,Mysql 自增ID是不是很容易看出你的销量,做个差值计算就好了
对比UUID
UUID是无序的,订单ID都加索引,在你插入数据的时候,维护B+树很好性能,需要频繁的调整叶子结点的数据,还会导致页分裂。简而言之就是性能不高,而雪花算法性能高低延迟
雪花算法缺点
由于时间戳占生成id的41 bit位,且这个时间戳是根据服务器时间生成的,一旦服务器时间回拨了一下,你就嗝屁了,可能会生成重复的 id
雪花算法改造
基于基因法改造案例如下,详情点击跳转 一文搞懂分库分表算法,通俗易懂(基因法、一致性 hash、时间维度)
//41 bit 位时间戳,5 bit 位数据中心,5 bit 机器位,5 bit累加位,7 bit 用户基因位long orderId = ((timestamp - twepoch) << 22)| (datacenterId << 17)| (workerId << 12)| (sequence << 7)| uid;
小咸鱼的技术窝
关注不迷路,分享更多技术干货B站、CSDN、微信公众号都是(小咸鱼的技术窝),更多详情在主页