分享好友 最新动态首页 最新动态分类 切换频道
【分布式技术专题】「分布式ID系列」百度开源的分布式高性能的唯一ID生成器UidGenerator
2024-12-28 14:51

UidGenerator是什么

UidGenerator是百度开源的一款分布式高性能的唯一ID生成器,更详细的情况可以查看官网集成文档

uid-generator是基于Twitter开源的snowflake算法实现的一款唯一主键生成器(数据库表的主键要求全局唯一是相当重要的)。要求java8及以上版本。

snowflake算法

Snowflake算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)。

将long的64位分为3部分,时间戳、工作机器id和序列号,位数分配如下

时间戳部分的时间单位一般为毫秒,也就是说1台工作机器1毫秒可产生4096个id(2的12次方)。

UidGenerator算法

与原始的snowflake算法不同,uid-generator支持自定义时间戳、工作机器id和序列号等各部分的位数,以应用于不同场景。

  • sign(1bit):固定1bit符号标识,即生成的UID为正数。
  • delta seconds (28 bits):当前时间,相对于时间基点"2016-05-20"的增量值,单位:秒,最多可支持约8.7年
  • worker id (22 bits):机器id,最多可支持约420w次机器启动。内置实现为在启动时由数据库分配,默认分配策略为用后即弃,后续可提供复用策略。
  • sequence (13 bits):每秒下的并发序列,13 bits可支持每秒8192个并发。

这些字段的长度可以根据具体的应用需要进行动态的调整,满足总长度为64位即可。

Snowflake和UidGenerator的对比

百度的worker id的生成策略和美团的生成策略不太一样,美团的snowflake主要利用本地配置的port和IP来唯一确定一个workid,美团的这种生成方式还是可以由于手工配置错误造成port重复,最终产生重复ID的风险,百度的这种生成方式每次都是新增的,可能会一段时间后worker id用完的情况,人工配置错误的可能性很小了。

源码分析

DefaultUidGenerator

DefaultUidGenerator的产生id的方法与基本上就是常见的snowflake算法实现,仅有一些不同,如以秒为为单位而不是毫秒。DefaultUidGenerator的产生id的方法如下。

 

nextId方法主要负责ID的生成,这种实现方式很简单,如果毫秒数未发生变化,在序列号加一即可,毫秒数发生变化,重置Sequence为0(Leaf文章中讲过,重置为0会造成如果利用这个ID分表的时候,并发量不大的时候,sequence字段会一直为0等,会出现数据倾斜

CachedUidGenerator

CachedUidGenerator支持缓存生成的id。

  • 【采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费】
  • 【UidGenerator通过借用未来时间来解决sequence天然存在的并发限制】
基本实现原理

正如名字体现的那样,这是一种缓存型的ID生成方式,当剩余ID不足的时候,会异步的方式重新生成一批ID缓存起来,后续请求的时候直接的时候直接返回现成的ID即可。

在实现上, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题. 最终单机QPS可达600万。

使用RingBuffer缓存生成的id。RingBuffer是个环形数组,默认大小为8192个,里面缓存着生成的id。

CachedUidGenerator采用了双RingBuffer,Uid-RingBuffer用于存储Uid、Flag-RingBuffer用于存储Uid状态(是否可填充、是否可消费)

由于数组元素在内存中是连续分配的,可最大程度利用CPU cache以提升性能。但同时会带来「伪共享」FalseSharing问题,为此在Tail、Cursor指针、Flag-RingBuffer中采用了CacheLine 补齐方式。

获取id

会从ringbuffer中拿一个id,支持并发获取

 
RingBuffer缓存已生成的id

RingBuffer为环形数组,默认容量为sequence可容纳的最大值(8192个,可以通过boostPower参数设置大小。几个重要的数据结构,采用了RingBuffer的方式来缓存相关UID信息。

tail指针、Cursor指针用于环形数组上读写slot

Tail指针

指向当前最后一个可用的UID位置:表示Producer生产的最大序号(此序号从0开始,持续递增)。Tail不能超过Cursor,即生产者不能覆盖未消费的slot。当Tail已赶上curosr,此时可通过rejectedPutBufferHandler指定PutRejectPolicy

Cursor指针

指向下一个获取UID的位置,其一定是小于Tail:表示Consumer消费到的最小序号(序号序列与Producer序列相同)。Cursor不能超过Tail,即不能消费未生产的slot。当Cursor已赶上tail,此时可通过rejectedTakeBufferHandler指定TakeRejectPolicy

Tail - Cursor表示的是现在可用的UID数量,当可用UID数量小于一定阈值的时候会重新添加一批新的UID在RingBuffer中。

填充id
  • RingBuffer填充时机
    • 程序启动时,将RingBuffer填充满,缓存着8192个id
    • 在调用getUID()获取id时,检测到RingBuffer中的剩余id个数小于总个数的50%,将RingBuffer填充满,使其缓存8192个id。
    • 定时填充(可配置是否使用以及定时任务的周期

因为delta seconds部分是以秒为单位的,所以1个worker 1秒内最多生成的id书为8192个(2的13次方)。从上可知,支持的最大qps为8192,所以通过缓存id来提高吞吐量。

为什么叫借助未来时间

因为每秒最多生成8192个id,当1秒获取id数多于8192时,RingBuffer中的id很快消耗完毕,在填充RingBuffer时,生成的id的delta seconds 部分只能使用未来的时间。(因为使用了未来的时间来生成id,所以上面说的是,【最多】可支持约8.7年

注意:这里的RingBuffer不是Disruptor框架中的RingBuffer,但是借助了很多Disruptor中RingBuffer的设计思想,比如使用缓存行填充解决伪共享问题。

填充RingBuffer
 

生成id(上面代码中的uidProvider.provide调用的就是这个方法)

 
RingBuffer的代码
 
代码层面的优化

代码中通过字节的填充,来避免伪共享的产生。

多核处理器处理相互独立的变量时,一旦这些变量处于同一个缓存行,不同变量的操作均会造成这一个缓存行失效,影响缓存的实际效果,造成很大的缓存失效的性能问题。下面图中线程处理不同的两个变量,但这两个变量的修改都会造成整个整个缓存行的失效,导致无效的加载、失效,出现了伪共享的问题

RingBuffer中通过定义一个PaddedAtomicLong来独占一个缓存行,代码中的实现填充可能需要根据具体的执行系统做一些调整,保证其独占一个缓存行即可。

take先关id的源码

下面我们来看下如何获取相关的UID

 

通过AtomicLong.updateAndGet来避免对整个方法进行加锁,获取一个可以访问的UID的游标值,根据这个下标获取slots中相关的uid直接返回 缓存中可用的uid(Tail - Cursor)小于一定阈值的时候,需要启动另外一个线程来生成一批UID UID 的生成

public synchronized boolean put(long uid) { long currentTail = tail.get(); long currentCursor = cursor.get();

 

获取Tail的下标值,如果缓存区满的话直接调用RejectedPutHandler.rejectPutBuffer方法 未满的话将UID放置在slots数组相应的位置上,同时将Flags数组相应的位置改为CAN_TAKE_FLAG CachedUidGenerator通过缓存的方式预先生成一批UID列表,可以解决UID获取时候的耗时,但这种方式也有不好点,一方面需要耗费内存来缓存这部分数据,另外如果访问量不大的情况下,提前生成的UID中的时间戳可能是很早之前的,DefaultUidGenerator应该在大部分的场景中就可以满足相关的需求了。

填充缓存行解决"伪共享"

关于伪共享,可以参考这篇文章《伪共享(false sharing,并发编程无声的性能杀手》

 
PaddedAtomicLong的设计
 

Spring Boot工程集成全局唯一ID生成器 UidGenerator

基础工程创建

官网集成文档

创建数据表

执行如下SQL

 

在使用的数据库中创建表WORKER_NODE。(如果数据库版本较低,需要将TIMESTAMP类型换成datetime(3),一劳永逸的做法就是直接将TIMESTAMP换成datetime(3))

引入Maven依赖
 
互联网jar包引入(本文用的是此方式)

在maven仓库只找到了一个jar包。

 
排除冲突的依赖

uid-generator中依赖了logback和mybatis。一般在项目搭建过程中,springboot中已经有了logback依赖,mybatis会作为单独的依赖引入。如果版本和uid-generator中的依赖不一致的话,就会导致冲突。为了防止出现这些问题,直接排除一劳永逸。

 

排除冲突的依赖如下:(使用本地项目引入的方式也需要排除以下依赖)

 

我这里用的是mybatis-plus,mybatis-plus官方要求的是,如果要使用mybatis-plus,就不能再单独引入mybatis了,所以我这里也是必须排除mybatis的。

配置SpringBoot核心配置

修改配置文件application.properties(注意MySQL地址、数据库名称账户等于之前建表的保持一致

 
@MapperScan的dao层接口扫描
核心对象装配为spring的bean。

uid-generator提供了两种生成器: DefaultUidGenerator、CachedUidGenerator。

如对UID生成性能有要求, 请使用CachedUidGenerator。这里装配CachedUidGenerator,DefaultUidGenerator装配方式是一样的。

自定义DisposableWorkerIdAssigner

将源码DisposableWorkerIdAssigner类加入到自己的项目中,并将其中的mapper方法修改成自己项目中的方法与启动类同级目录新建DisposableWorkerIdAssigner内容如下

 
 
详细配置信息控制
 
mapper服务接口

与启动类同级目录新建WorkerNodeMapper内容如下

 
WorkerNodeMapper
 
创建UidGenService逻辑类

最新文章
WordPress简约清新文艺范博客主题(自适应PC+手机站)
简约清新文艺范博客wordpress主题又是一款博客型的小众Wordpress自媒体模板,喜欢的朋友能够拿下。响应式自适应各种移动设备,同一个后台,数据即时同步,简略适用!原创规划、手动编写HTML+CSS,兼容IE浏览器、火狐浏览器、谷歌浏览器、36
笔灵AI,你的智能PPT助手,一键生成精美幻灯片,让创意自由飞翔
相信大家在工作中一定遇到这样的情况: 自己费了九牛二虎之力,各种找素材、选模板 、买资料、连熬几个大夜精心制作的PPT 却被老板轻飘飘一句:感觉还差一点 打入冷宫!!! 同一个世界,同样的
【5261(电信4G)WiFi万能钥匙下载】酷派5261 电信4GWiFi万能钥匙5.1.02免费下载
下载WiFi万能钥匙,认准官方正版!免费安全WiFi热点,无忧一键连接。日常任务随时做,轻松赚现金外快~【积分赚钱】完成简单日常任务,轻轻松松赚现金【安全免费】随时随地免费连接,畅享安全免费WiFi,节省流量快人一步!【权威认证】通过
流产药微信联系方式_(官方商城药店旗舰店)第一位发货+正品包邮
流产药微信联系方式_(官方商城药店旗舰店)第一位发货+正品包邮流产药微信联系方式信赖网上药库,安全药品满仓。顺丰快捷配送,货到付款安心。隐私严格守护,健康悄然到家。真伪轻松核验,假一赔百践行。专业药师贴心指导,正确用药更健康。
淘宝店铺转让需要什么手续?淘宝店铺怎么交易?
是卖家需要准备的材料。如果卖家是店铺注册的本人,那么就需要提供卖家本人的身份证等相关证件。物竞天择,适者生存在如今的电商平台市场也是一样的,很多人会选择交易店铺,也有人一直持续经营,也有刚刚要创业的店铺,那么淘宝店铺交易需
百度·问答大礼包,全网首发最新最全攻略,带你走进月入过万快车道!
百度·问答大礼包,全网首发最新最全攻略,带你走进月入过万快车道!作为答主,您是否一直苦于收益不高?是否想要突破瓶颈但不知道如何实现?本文提供最新最全的百度问答攻略,从平台规则到题目优化,教你快速提升答主收益,踏上月入过万的
人工智能系统的四要素是什么
人工智能系统的四要素为:1、大数据;人工智能的智能都蕴含在大数据中。2、算力;为人工智能提供了基本的计算能力的支撑。3、算法;实现人工智能的根本途径,是挖掘数据智能的有效方法。4、场景;对大量数据进行预处理。人工智能四要素分析
济宁百度爱采购托管代运营
爱采购必须作为企业或个体工商户加入,需要提供正式真实的营业执照,签订合同哦!目前百度爱采购有两种入驻模式,一种是采购推广入驻(招标CPC模式),另一种是认证企业入驻(按年收费,点击不收费)。您可以根据企业的实际应用需求和行业的实
重庆金美通信申请基于 OSPF 面向业务 QoS 需求的自适应链路构建专利,满足用户 QoS 需求
金融界 2024 年 12 月 13 日消息,国家知识产权局信息显示,重庆金美通信有限责任公司申请一项名为“一种基于 OSPF 面向业务 QoS 需求的自适应链路构建方法及系统”的专利,公开号 CN 119109861 A,申请日期为 2024 年 8 月。专利摘要显示
淘宝店推广怎么做?作者:小果 时间:2024-12-20 阅读:3523
淘宝店推广怎么做?哪些方法有效?在竞争激烈的电商市场中,淘宝店铺的推广成为了卖家们必须面对的重要课题。通过有效的推广策略,不仅可以提高店铺的曝光度和流量,还能显著提升销量和转化率。本文将介绍几种有效的淘宝店推广方法,帮助卖
相关文章
推荐文章
发表评论
0评