对雪花算法的学习
- Snowflake IDs或者snowflakes 生成分布式全局ID的算法
- Twitter创建,用于推文的ID,京东仓储平台生成ID是用的雪花算法修改后的版本
- 生成的ID分布式唯一且按时间递增有序,毫秒数在高位,自增序列在低位,ID都是趋势递增
- 不依赖数据库等第三方系统,稳定性尤其是性能非常高
- 根据自身业务特性分配bit位,非常灵活
其他分布式唯一ID生成方案
-
数据库生成
- 字段默认自增(设置auto_increment来生成全局唯一ID)
- 优点:
- 简单,维护成本低
- ID唯一且单调递增,并且可以设置固定步长
- 缺点:
- 可用性难以保证
- 每次生成ID都要访问数据,瓶颈在于数据库实例的读写性能
- 数据库挂了就会导致服务不可用
-
UUID
-
标准式:由一组32位数的16进制数字所构成
- 以连字号分为五段,形式为8-4-4-4-12的32个字符
- e.g.550e8400-e29b-41d4-a716-446655440000
-
总数:16^32=2^128,差不多3.4 x 10^38
-
每纳秒产生1M个UUID,要花100亿年才能将所有UUID用完
-
优点
- 本地生成ID,不需要rpc调用,没有网络上的耗时
- 没有性能上限
-
缺点
- 可读性差
- 长度过长,16字节128位,生成UUID通常是36位(包括了-),某些场景不适用
- 数据库主键的话,会让InnoDB引擎下长度过程,二级索引会占据很大空间
- 无法保证趋势增性,在存储引擎下,新插入数据会根据主键找合适位置,会导致频繁的移动、分页的开销增多
-
snowflake
-
算法生成的id通常位64位,java中用long类型(8字节)
-
- 符号位 1 即范围 [-2^(64-1), 2^(64-1)] (不包括id)
- 41时间位(单位毫秒):1 x 2^41 / (1000 x 3600 x 24 x 365) = 69年
- 10bit机器位 [0,1023]
- 12-bit表示1ms内自动递增的序列号,1 x 2^12 = 4096个 范围[0,4095]。
- 单机1ms可以生成4096个不重复的ID!!!
-这种方式可以支撑大部分业务,如果有划分IDC的需求,可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器。
- 可以做后续调整,如果时间位为秒级,省出10bit留给机器位
- 1-31-22-10,时间变为1 x 2^31/ (3600 x 24 x 365) = 68年
- 通过上述方式进行生成ID,可以保证4194303台机器在任意68年的时间段里不会出现重复的ID,而且单台机器支持一秒能够生成1024个ID。
- 最大ID:maxInstanceId = -1 ^( -1<<10),补码方式
计算序列号
- 如果上个生成ID的时间位与当前ID的时间位冲突,则会生成一个序列号进行区分,如果序列号用尽,则等待下一个时间点再生成。如果上个生成ID的时间位与当前ID的时间位不冲突,则将序列号设置成0。
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//通过掩码获取序列号,超过序列后等待下一个时间点
if (sequence == 0) {
timestamp = tilNextSecs(lastTimestamp);
}
}
else {
//上一个时间戳和当前时间戳,出现不同,生辰一个序列号区分
sequence = 0L;
}
生成 ID
((timestamp - from) << timestampLeftShift) // (当前时间 - 起始时间) 向左移位
| (instanceId << instanceIdShift) // 机器位 向左移位
| sequence; // 序列位
//((timestamp - from) << timestampLeftShift) 计算时间位
from是固定的1422720000000,范围取值:[0, 2^41 - 1],from解释:相对时间,以from为起始来算的过去了多少时间
注意事项
- 机器位取值
- 主机唯一标识,运维平台取,但不一定能存下以及唯一标识后续改变
- ip进行计算,最大255.255.255.255,采用IP段数值相加<1024即可生成机器位,不受ip位限制,这种方式一定要保证网段的相同,不然可能出现重复
- redis或者数据库或者zk协调,在应用启动的时候给每个机器分配不会重复的机器位id
- 单例方式生成ID,会依赖上一次生成的ID的时间来判断是否需要对序列号进行增加的操作,如果不是单例,两个业务用两个对象同时获取ID,则可能会生成相同的ID
- 雪花算法强依赖时间,如果时间发生回拨,有可能会生成重复的ID,在我们上面的nextId中我们用当前时间和上一次的时间进行判断,如果当前时间小于上一次的时间那么肯定是发生了回拨
- 雪花算法的做法是简单的抛出了一个异常,我们自己可以通过配置等待时间,让机器时间追回来
- 若出现ID重复可以通过逆运算进行问题定位
总结
- 之前一直听说雪花算法,今天了解了一下感觉确实有它的实际应用(比如机器位取值,利用到时间戳这些系统上的东西)