1. 背景
最近项目需要有分布式ID生成器这样的组件,利用生成的ID作为表的主键而不是mysql的自增ID
查阅了一些网上的一些资料,针对性的思考,整理成文。
2. 组件要求
高可用
工业生产需要,比如单机挂了影响服务可不行
2.1 全局唯一
作为ID的特性
2.2 尽量保证ID递增(并无严格要求)
查询的时候有分页或者排序类似的需求,如果主键ID本身能体现出时序效率会更
查询的时候,往往有分页或者排序的需求
1.可以给添加一个时间字段,并在其上建立普通索引
2.ID按照时间粗略有序
但是普通索引的访问效率比聚集索引慢,所以倾向于方案2
但是做不到严格排序,因为往往要求各个机器以master-slave形式交互,影响可用性以及QPS
2.3 其他
1.ID尽可能的短
减少存储的空间以及增加查询的效率,但是往往都按照64位来算就足够
2.可用的时间足够久
一些类SNOWFLAKE的算法会在64位的ID中利用部分位数(如41)代表时间戳,当这部分时间戳的空间用完了,这个服务就不work了
3.容错性
假如一台机器的时间机器被人工往前调了怎么办
4.允许批量生成
允许业务方batch请求一次拿多个ID,提高效率
5.QPS尽可能高
类SNOWFLAKE算法会在64位ID中利用部分位数(如12)表示单位时间内生成的ID序号,这部分序号用完了,这个单位时间就不能再生成序号了
3.思路
3.1几种方案
1.利用mysql表 主键ID auto_increment的特性
缺点:主从同步,受限于主库的写入性能
2.UUID
缺点:完全不保证递增
3.获取当前时间
缺点:不管时间是精确到秒还是微秒,都代表一个单位时间只能生成一个ID,无法满足批量的请求
4.类SNOWFLAKE算法
本文主要针对该类算法进行讲解
3.2 SNOWFLAKE算法
先对SNOWFLAKE算法简单讲解,参照下面的refer讲解的
图是抄来的
思路如下:
1.一个ID由64位生成
2.41bit作为时间戳,记录当前时间到标记的起始时间(如到2018.1.1)差,精确到毫秒,那么服务可用时长为(1<<41)/(1000* 60 * 60 * 24 *365) = 69.73年
3.10bit作为机器ID,也就是可以有1024台机器
4.12bit作为序列号,代表单位时间(这里是毫秒)内允许生成的ID总数,也就是1ms内允许生成4096个ID
代码可以直接参考refer
如何满足分布式ID生成器的要求
高可用:1024台机器互不影响的工作,单台挂就挂
全局唯一:省略
尽量保证ID递增:时间戳放在高位
可用时间长度:69.73年
容错性:下面再探讨
允许批量生成:同一单位时间(ms)内,最多允许生成4096个ID,支持批量
QPS尽可能高:在ID设计上,只能代表1ms最多允许有4096个ID生成,但实际用起来,其实看代码的设计,并发,锁控制等等,
3.3 类SNOWFLAKE算法
SNOWFLAKE给出的主要是一个思想,把ID划分为多个段,有不同的含义,可以结合自己的要求进行重新划分。按照个人理解,时间戳位数少了,机器位数多了,序列号位数多了。
3.3.1 时间 or 时间差
上面SNOWFLAKE算法是时间差来算的,也就是67.93年,换种思路,用绝对时间,要求位数开大一点,不用每次都减去初始时间。
假设开到42位,精确到ms,那么就是 2^42=4398046511104,去https://currentmillis.com/ 查询,够用到2109年
3.3.2 单位时间的序号上限
SNOWFLAKE算法里面1ms最多允许有4096个ID生成。但是代码实际执行过程中,性能瓶颈以及现实场景要求,往往不会要求这么高的位数,可以改成256个,这样就是QPS上限256000了(空间上允许,真正执行还是取决于代码效率),基本不会要求这么高。也就是序号需要8位就够
3.3.3 工作机器个数
原算法支持1024台,当然看各业务需求了,个人感觉4台都够了,想想4*256000=100w,1s生成100w个ID(还是那句话,QPS取决于代码执行效率),考虑机器挂掉以及代码执行性能,32台也满足大部分公司要求了。也就是5位ID给机器
3.3.4 拓展字段
上面一共42位(时间戳) + 8位(序列号) + 5位(机器ID) =55位,还有9位可以作为其他用途
标志业务线
比如公司有A业务线,B业务线,xx,更有甚者是两级的业务线A.a1业务,A.a2业务。反正根据要求进行一个映射,适合要求即可,假定这里分配32个业务线(5位)
3.3.5 保留字段
还剩下4位不知道有啥用,保留用吧。
有的文档写可以标明机房ID,有的写可以为后续业务拓展预留,比如32台机器不够用了要64台,比如1ms要512个ID了而不是256个ID
保留字段是用于这些未来发展可能要用的
4.方案讨论
4.1 ID为何不能严格有序
因为各个机器保证不了时间同步,即使有NTP server,也保证不了ms内各个机器同步
4.2 时间回退了怎么办,比如人为修改了系统时间
每个机器记录生成的最后一个ID,新生成ID的时候,拿当前时间和最后一个ID解析出来的时间(高42位)进行对比,不合理就报错
4.3 其他优化
比如并发控制,CAS,锁等,这里不展开代码细节的讨论
4.4 单机能生成全局唯一id吗
不能,因为没有单机标识,无论时间戳,随机数,所有机器都是等价的
MAC地址有用吗?有,但是MAC地址可以伪造,无法保证唯一
所以一定要有一个分布式集群统一分配机器编号来保证
5.总结
本文主要介绍了ID生成器的一些思路,并且对SNOWFLAKE方法进行简单讲解,从64位的构成方式上进行调整,进行针对性的改动,适合自己的要求。来达到分布式ID生成器组件的各个要求。对于方案设计提出一些思路
refer
https://www.jianshu.com/p/955909e1bd71 对于各种背景都有介绍
https://tech.meituan.com/MT_Leaf.html 美团的实现
https://soulmachine.gitbooks.io/system-design/content/cn/distributed-id-generator.html
https://blog.csdn.net/u010372981/article/details/68924830 类snowflake的算法,代码直白易懂
网友评论