雪花算法
ID作为系统基础,而在ID的选择上,需要根据实际情况来选择,没有哪一种ID是完美无缺的。
雪花算法作为近年流行的分布式顺序主键,缺点是网络消耗,时间回调,多服务器时间同步等。
1、雪花算法的优缺点
本文就不讲解雪花算法的来源了,我们先来看看各种ID的对比图。
自增ID | UUID | 雪花算法 | |
---|---|---|---|
MySQL | √ | × | √ |
分布式 | × | √ | √ |
是否存在网络消耗 | × | × | √ |
时间回调 | √ | √ | × |
多服务器时间同步 | √ | √ | × |
※提示※
在MySQL中,最重要的是InnoDB,其核心算法是B+树。通过B+树对数据insert、query操作时,数据主键的顺序性对于性能而言,非常关键(再挖一坑,不定时填坑)。
2、雪花算法的原理
2.1、引入
多台设备如何生成顺序的ID?也就是说ID中应该包含哪些信息?
很显然,应该包含时间、设备号、序列号
雪花算法就是由上述元素构成的。
例子:时间戳(1655458773571)、设备(1000)、序列号(1000)
如果我们将例子中的元素拼接起来,即可实现一个ID(1655458773571 1000 0001)。这个ID的位数是21位,而long的最大值为9223372036854775807,ID已经超过了最大long值。
这个时候,有童鞋会说时间戳(13位)+设备号(3位)+序列号(2位)不是也可以吗?当然可以,只是存储能力下降了。
雪花算法的主要工作就是压缩空间,关于压缩空间,雪花算法和redis中的位图是同样的思路。
2.2、实现原理
关于如何压缩空间,举个例子
例子:目前有3个3L的桶,第一个桶装水2.5L,第二个桶装水1.5L,第三个桶装水2L,是否可以腾出一个空桶?
很显然,可以。
目前我们已知的雪花算法构成是:
时间戳(1655458773571)
设备(1000)
序列号(1000)
而long最大可存的数字为9223372036854775807(19位)
众所周知,long数据类型占8个字节构成,每个字节占8bit。也就是一个雪花算法的ID是由64个bit构成。
如果我们将时间戳、设备、序列号都装入long类型的桶中,会是什么样呢?
时间戳(1000000000000000000000011000000101110001000010000011101001000011)实际有意义的是后41位
设备 (1000000000000000000000000000000000000000000000000000001111101000)实际有意义的是后10位
序列号(1000000000000000000000000000000000000000000000000000001111101000)实际有意义的是后10位
不难发现,每一个long类型的桶中都有很多空气0,如果将空气0通过移位即可存入一个long类型桶中,即 1+41位+10位+10位 = 62位 < long的64位
我们可以表示为:
1 11000000101110001000010000011101001000011 1111101000 1111101000 00
第一位 1,表示正负值
2-42位,时间戳
43-52位,设备
53-62位,序列号
最后还多余两位 补充了 0
至此,我们已经将雪花算法的原理介绍清楚了。
目前我们已知long类型可以存储64位bit,第一位表示正负值,为0,41位表示时间戳,还剩下22位。
那么22位中,需要几位来表示设备,需要几位来表示序列号,自定义即可。
3、雪花算法的实现(Java)
关于代码,网上有写的更好的请参考理解分布式id生成算法SnowFlake - SegmentFault 思否
4、总结
计算机回归至底层是0和1,而为了人类理解方便,故而会存在补位0的情况。
在某些特定的场合,作为编程人员,是需要回归到0和1,才能写出更好的程序。