唯一ID生成算法剖析,看看这篇就够了( 三 )

唯一ID生成算法剖析,看看这篇就够了
文章插图
如图所示,可保证每台数据库生成的ID是不冲突的,但这种固定步长的方式也会带来扩容的问题,很容易想到当扩容时会出现无ID初始值可分的窘境,解决方案有:

  • 根据扩容考虑决定步长
  • 增加其他位标记区分扩容
这其实都是在需求与方案间的权衡,根据需求来选择最适合的方式 。
 
2.批量生成一批ID如果要使用单台机器做ID生成,避免固定步长带来的扩容问题,可以每次批量生成一批ID给不同的机器去慢慢消费,这样数据库的压力也会减小到N分之一,且故障后可坚持一段时间 。
唯一ID生成算法剖析,看看这篇就够了

文章插图
如图所示,但这种做法的缺点是服务器重启、单点故障会造成ID不连续 。还是那句话,没有最好的方案,只有最适合的方案 。
雪花算法
定义一个64bit的数,对指定机器 & 同一时刻 & 某一并发序列,是唯一的,其极限QPS约为400w/s 。其格式为:
唯一ID生成算法剖析,看看这篇就够了

文章插图
唯一ID生成算法剖析,看看这篇就够了

文章插图
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 —— 使用雪花算法(如对存储空间、查询效率、传输数据量等有较高要求的场景)
对于最初我们定义的唯一ID特性,各方案的对比如下:
唯一ID生成算法剖析,看看这篇就够了

文章插图
从冲突率、QPS和算法时间复杂度来比较的话:
唯一ID生成算法剖析,看看这篇就够了

文章插图
参考
UUID算法分析
关于UUID的二三
UUID百度百科
UUID唯一资源命名空间的来龙去脉
UUID是如何保证唯一性的?
如果再有人问你分布式 ID,这篇文章丢给他
分布式唯一ID的几种生成方案
UidGenerator-百度
Leaf——美团点评分布式ID生成系统


推荐阅读