文章插图
如图所示,可保证每台数据库生成的ID是不冲突的,但这种固定步长的方式也会带来扩容的问题,很容易想到当扩容时会出现无ID初始值可分的窘境,解决方案有:
- 根据扩容考虑决定步长
- 增加其他位标记区分扩容
2.批量生成一批ID如果要使用单台机器做ID生成,避免固定步长带来的扩容问题,可以每次批量生成一批ID给不同的机器去慢慢消费,这样数据库的压力也会减小到N分之一,且故障后可坚持一段时间 。
文章插图
如图所示,但这种做法的缺点是服务器重启、单点故障会造成ID不连续 。还是那句话,没有最好的方案,只有最适合的方案 。
雪花算法
定义一个64bit的数,对指定机器 & 同一时刻 & 某一并发序列,是唯一的,其极限QPS约为400w/s 。其格式为:
文章插图
文章插图
将64 bit分为了四部分 。其中时间戳有时间上限(69年) 。机器id只有10位,能记录1024台机器,常用前几位表示数据中心id,后几位表示数据中心内的机器id 。序列号用来对同一个毫秒之内的操作产生不同的ID,最多4095个 。
这种结构是雪花算法提出者Twitter的分法,但实际上这种算法使用可以很灵活,根据自身业务的并发情况、机器分布、使用年限等,可以自由地重新决定各部分的位数,从而增加或减少某部分的量级 。比如百度的UidGenerator、美团的Leaf等,都是基于雪花算法做一些适合自身业务的变化 。
由于雪花算法是强依赖于时间的,在分布式环境下,如果发生时钟回拨,很可能会引起id冲突的问题 。解决方案有:
- 将ID生成交给少量服务器,并关闭时钟同步 。
- 直接报错,交给上层业务处理 。
- 如果回拨时间较短,在耗时要求内,比如5ms,那么等待回拨时长后再进行生成 。
- 如果回拨时间很长,那么无法等待,可以匀出少量位(1~2位)作为回拨位,一旦时钟回拨,将回拨位加1,可得到不一样的ID,2位回拨位允许标记三次时钟回拨,基本够使用 。如果超出了,可以再选择抛出异常 。
可以发现,常用的分布式唯一ID生成思路基本是利用一个长串数字或字符串,将其分割成多个部分,分别记录时间信息、机器/名字信息、随机信息、序列信息等 。时间信息部分决定了该策略能使用的时长,机器/名字信息支持了分布式环境下的独自生成唯一ID与识别能力,序列信息保证了事件的顺序记录以及同一时间单位下的并发数,而随机信息则加大了ID整体的不可识别性 。
实际上如果现有的方法依然不能满足,我们完全可以依据自身业务和发展需求,来自行决定使用何种策略生成唯一ID 。各种方案都有其优缺点,技术的使用没有绝对的好坏之分,主要在于是否适合使用场景:
- 要求生成全局唯一且不会重复ID,不关心顺序 —— 使用基于时间的UUID(如游戏聊天室中不同用户的身份ID)
- 要求生成唯一ID,具有名称不可变性,可重复生成 —— 使用基于名称哈希的UUID(如基于不可变信息生成的用户ID,若不小心删除,仍可根据信息重新生成同一ID)
- 要求生成有序且自然增长的ID —— 使用数据库自增ID(如各业务操作流水ID,高并发下可参考优化方案)
- 要求生成数值型无序定长ID —— 使用雪花算法(如对存储空间、查询效率、传输数据量等有较高要求的场景)
文章插图
从冲突率、QPS和算法时间复杂度来比较的话:
文章插图
参考
UUID算法分析
关于UUID的二三事
UUID百度百科
UUID唯一资源命名空间的来龙去脉
UUID是如何保证唯一性的?
如果再有人问你分布式 ID,这篇文章丢给他
分布式唯一ID的几种生成方案
UidGenerator-百度
Leaf——美团点评分布式ID生成系统
推荐阅读
- 算法:如何实现大正整数相加?
- 用python实现汉诺塔算法!(含代码示例)
- 自媒体1w推荐量却只有100多阅读量?莫慌!平台算法了解一下
- 太阳系中拥有卫星最多的行星是天王星 阳系中唯一同时拥有大陆地壳和大洋地壳的行星是
- Mysql 为什么要选择 B+Tree
- 分享mysql配置文件my.cnf一键生成器
- 分布式原理:一致性哈希算法简介
- DH算法原理
- 算法之常见排序算法-冒泡排序、归并排序、快速排序
- 普洱茶养生成为引领健康生活方式的风向标