分布式id生成算法
背景
在分布式中有很多id要保证是全局唯一的,比如订单id什么的,首先业务上需要唯一,其次遇到分库分表这种,不同的库表中如果只用该表自增的,那么表之间的自增id会重复,因此需要一个方案来生成全局唯一性id
分布式id的要求如下:
1、需要全局唯一
2、需要高性能
3、需要高可用
4、需要好接入
5、递增
常见的算法如下:
数据库自增
专门用一个独立的表主键id自增,作为唯一id,这样是完全可以保证唯一的,但是id长度不一致,在一些场景中,还是需要保证id的长度位数,另外,需要等插入数据库,这个id才知道是什么
最大的缺点就是数据库本来就是系统的瓶颈,在高并发场景下单点db不太适合抗住高并发
可以设置基于数据库集群模式,每台机器初始值不一样,比如一个是0,一个是1,并且递增gap是2,那么两个肯定不会重复,但是不容易扩展,而且这样也不能百分百满足全局递增
redis递增
redis毕竟只是个缓存,拿来做全局递增,有一点微小风险,另外, 如果模式设置不正确,可能会造成重复。
如果是RDB,那么可能会挂掉了但是没有及时存储最新导致id重复
如果是AOF,那么重启恢复时间过长
号段模式
也是利用数据库,但是不同的是,不是每次自增一个来生成一个分布式id,而是一次生成批量个,并且一个业务只需要一条记录,表结构如下,[max_id, max_id+step)就是当前生成的批量id,业务方就可以拿到自己的内存中自己进行处理
CREATE TABLE id_generator (
id int(10) NOT NULL,
max_id bigint(20) NOT NULL COMMENT '当前最大id',
step int(20) NOT NULL COMMENT '号段的布长',
biz_type int(20) NOT NULL COMMENT '业务类型',
version int(20) NOT NULL COMMENT '版本号',
PRIMARY KEY (`id`)
)
snowflake算法
snowflake算法是twitter开源的一个分布式id生成方法,算是目前应用比较广发的分布式id算法
snowflake算法其实也很简单,甚至按照自己的角度也能梳理出来,原理如下:
1、首先,分布式id应该是64位,因为32位太小了
2、64位情况下,显然既然要唯一,那么可以根据多个维度进行填充,首先,时间戳,就可以保证唯一,另外,节点可以再去保证不同节点同时并发的唯一性(节点可以看作一个服务的多个实例)
3、同一个节点,为了同一毫秒下保证并发id唯一,需要加上锁
如下图所示
总共64个bit,第一个0表示正数,41个bit表示带有毫秒的时间戳(可以自己去试下,时间戳转成二进制确实是41bit), 10bit是工作机器id,也就是允许的最大实例id,后面12bit表示同一个毫秒内单机并发递增是最大能够是2^12=4095
也就是snowflake同意毫秒下支持生成的全局唯一id个数为 2^12 * 2 ^ 10 = 4194304
时钟回拨问题解决思路
1、如果服务所在机器发生了时钟回拨,那么可能会发生id重复,解决办法是,每次生产,都记住最新的时间,下次生成时再对比时间是否更小了,如果更小,则for循环,一直等待知道时间大于上次时间。 但是这个只能用于时间间隔很小的,如果高并发场景,那么显然不太适合
2、另一种方式是百度的UidGenerator方式,不适用时间戳,使用一个自增数就行
3、加入一个新的位,时钟位,可以设置为4位,相应地,调小一点工作节点位和序列号位。 每次发现时钟回拨,也就是当前时间比上一次生成算法的时间还小,并且间隔还比较大,直接等待不太现实,那么就将时钟位位数加1。 但是这样也有缺点,就是不能保证递增
4、Butterfly 算法 https://www.yuque.com/simonalong/butterfly/tul824,跟第二种其实是一个道理
工作id分配和回收
目前机器id需要每台机器不一样,这样的方式分配需要有方案进行处理,同时也要考虑,如果改机器宕机了,对应的workerId分配后的回收问题
可以采用redis自增方式,再做映射,或者使用zk,db
uid-generator
百度开源的,https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md
主要特点是使用了RingBuffer数据结构预先生成了uid(生成的时机为初始化时,已经使用的超过了阈值时)并缓存
Leaf
leaf是美团开源的,可切换成两部分: leaf-segement, leaf-snowflake
leaf-segement
leaf-snowflake
leaf-snowflake跟原生snowflake结构一致,特点在于
使用了zk来生成工作id,并且定时去上报时间系统时间
下次启动时,会获取zk上上报的时间,与当前时间比较,判断是否做了机器时钟回调
leaf默认老的节点重新启动,会在zk中永久生效
https://tech.meituan.com/2017/04/21/mt-leaf.html