当前位置:   article > 正文

分布式ID生成算法选型

分布式id生成选型

IM聊天消息的多端同步、消息顺序保证等是设计IM消息系统需要考虑的问题,也是后台开发面试重点,这里整理一下之前的笔记。

消息ID需要保持有序性、唯一性,用于系统检索、标识,需要一个高可用、高可靠的消息序列号生成器来产生同步数据,且序列号调用耗时要满足业务要求。

消息系统ID号要求

1、全局唯一性:作为消息的唯一标识,这是最基本的要求;

2、单调递增:一旦出现回退必然导致各种数据错乱、消息消失

3、安全性:对于部分电商系统要求消息ID无序性,比如订单号。如果单调连续递增,那么竞对很容易探取到公司每天的业务量。

消息序列号生成器要求

平均延迟和TP99延迟都要尽可能低;

可用性5个9;

满足业务需求的高QPS。

常见的自增ID方法有以下几种:

UUID

32个16进制数字,形式为8-4-4-4-12的36个字符。业界有标准的UUID生成规范,如1024Tools工具网站:

 

但是UUID字长128位,不方便存储;而且UUID的无序性可能会引起数据位置频繁变动,影响性能,不适合作为mysql主键。

Snowflake算法

 

Snowflake算法使用一个64 位的数字作为全局唯一ID,其中41位记录时间戳(毫秒);10位用来记录工作机器id;12位序列号,用来记录同毫秒内产生的不同id。

这个算法可以保证同一台机器在同一毫秒内生成一个唯一的 ID。可能一个毫秒内会生成多个 ID,但是有最后 12 个 bit 的序号来区分开来。同一时间戳(毫秒)内产生的4095个ID序号。

Snowflake不依赖数据库等第三方系统,部署方便,但是强依赖机器时钟。如果机器发生时钟回拨,那么会导致发号重复或者服务会处于不可用状态。

数据库自增ID

业务可以调用如下事务获取递增ID:

 

备注:REPLACE与INSERT语句很相似,区别在于:假如表中的一个旧记录与一个用于PRIMARY KEY或一个UNIQUE索引的新记录具有相同的值,则在新记录被插入之前,旧记录被删除。REPLACE语句要求表有一个PRIMARY KEY或UNIQUE索引,常用于获取自增ID。

使用数据库的话主要是维护方便(因为一般的公司都有专门的SRE团队),但是需要解决好DB单点性能问题,防止DB异常导致整个业务系统不可用。

以mysql为例,假如我们有n台机器,那么自增步长需要设置为n,每台机器的初始值依次为0,1,2…N-1,系统取号时可以将请求分散到n台机器上。

该方案虽然能满足性能要求,但是扩展比较麻烦,而且不能保证ID的全局单调递增,需要继续改进才能上生产。

微信数据版本号生成机制

对于IM系统而言,实际上要解决消息的唯一性、顺序性问题,可以将一个技术点分解成两个:将原先每条消息只有一个自增且唯一的消息ID,可以分拆成两个关键属性——消息ID(msgId)、消息序列号(seqId)。消息ID只要保证唯一性而不需要兼顾顺序性,消息序列号只要保证顺序性而不需要兼顾唯一性。

微信的消息序列号生成器对数据库自增ID方案做了如下改变:

原方案每次获取ID都得读写一次数据库,数据库压力较大。使用代理服务批量获取,即每次获取一个segment(如1000个)号段的值。用完之后再去数据库获取新的号段,读写数据库的频率从1减小到了1/step,数据库压力减轻;

不同业务、不同的发号需求使用biz_tag字段来区分,每个biz_tag的ID获取相互隔离。该方法扩容也方便,只需要对biz_tag分库分表就行。

该方案还是有一些缺陷,比如当号段使用完之后还是会阻塞在数据库的I/O上,TG999数据会出现偶尔的尖刺。如果希望DB取号段的过程能够做到无阻塞,也就不能在DB取号段的时候阻塞请求线程,可以采用双buffer的方式,使用两个号段缓存区segment。当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段。

 

备注:双buffer解决方式在很多场景都有用,比如logcxx中写log的实现方案。

全局ID的安全性

对于电商而言,不需要像IM系统那样保持有序性,只需要唯一性就行,不然的话竞对每天读取一次ID就能根据差值反解出公司每天的业务量。

可以对前面提到的开源的snowflake方案进行改进,解决时钟回退的问题。大致方案如下:

1、借助zk的服务注册机制,服务启动时首先检查自己是否写过zk主节点;

2、若写过,则用自身系统时间与主节点节点记录时间做比较,若小于主节点时间则认为机器时间发生了回拨,服务启动失败并报警;

3、若未写过,证明是新服务节点,直接创建持久节点并写入自身系统时间;

4、接下来综合对比其余服务节点的系统时间来判断自身系统时间是否准确。通过RPC请求得到所有服务节点的系统时间,查看自身系统时间是否处于所有系统阈值范围内;

5、如果大于阈值,认为本机系统时间发生偏移,启动失败并报警;

6、每隔一段时间(3s)上报自身系统时间写入zk主节点。

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/202699
推荐阅读
相关标签
  

闽ICP备14008679号