UidGenerator是什么

UidGenerator是百度开源的一款分布式高性能的唯一ID生成器,更详细的情况可以查看github:uid-generator,里面有更详细的介绍文档说明,其也是基于snowflake模型的一种ID生成器,我们再来回顾以下雪花模型。

Snowflake算法描述:指定机器 & 同一时刻 & 某一并发序列,是唯一的。据此可生成一个64 bits的唯一ID(long)。默认采用上图字节分配方式:

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

DefaultUidGenerator

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

 protected synchronized long nextId() {long currentSecond = getCurrentSecond();// Clock moved backwards, refuse to generate uidif (currentSecond < lastSecond) {long refusedSeconds = lastSecond - currentSecond;throw new UidGenerateException("Clock moved backwards. Refusing for %d seconds", refusedSeconds);}// At the same second, increase sequenceif (currentSecond == lastSecond) {sequence = (sequence + 1) & bitsAllocator.getMaxSequence();// Exceed the max sequence, we wait the next second to generate uidif (sequence == 0) {currentSecond = getNextSecond(lastSecond);}// At the different second, sequence restart from zero} else {sequence = 0L;}lastSecond = currentSecond;// Allocate bits for UIDreturn bitsAllocator.allocate(currentSecond - epochSeconds, workerId, sequence);}

CachedUidGenerator

正如名字体现的那样,这是一种缓存型的ID生成方式,当剩余ID不足的时候,会异步的方式重新生成一批ID缓存起来,后续请求的时候直接返回现成的ID即可。
我们来看一下这种方式下几个重要的数据结构,采用了RingBuffer的方式来缓存相关UID信息。
Tail: 指向当前最后一个可用的UID位置
Cursor: 指向下一个获取UID的位置,其一定是小于Tail
Tail - Cursor表示的是现在可用的UID数量,当可用UID数量小于一定阈值的时候会重新添加一批新的UID到RingBuffer中。

代码层面的优化

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

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

RingBuffer中通过定义一个PaddedAtomicLong来独占一个缓存行,代码中的实现填充可能需要根据具体的执行系统做一些调整,保证其独占一个缓存行即可。
下面我们来看下如何获取相关的UID

public long take() {// spin get next available cursorlong currentCursor = cursor.get();long nextCursor = cursor.updateAndGet(old -> old == tail.get() ? old : old + 1);// check for safety consideration, it never occursAssert.isTrue(nextCursor >= currentCursor, "Curosr can't move back");// trigger padding in an async-mode if reach the thresholdlong currentTail = tail.get();if (currentTail - nextCursor < paddingThreshold) {LOGGER.info("Reach the padding threshold:{}. tail:{}, cursor:{}, rest:{}", paddingThreshold, currentTail,nextCursor, currentTail - nextCursor);bufferPaddingExecutor.asyncPadding();}// cursor catch the tail, means that there is no more available UID to takeif (nextCursor == currentCursor) {rejectedTakeHandler.rejectTakeBuffer(this);}// 1. check next slot flag is CAN_TAKE_FLAGint nextCursorIndex = calSlotIndex(nextCursor);Assert.isTrue(flags[nextCursorIndex].get() == CAN_TAKE_FLAG, "Curosr not in can take status");// 2. get UID from next slot// 3. set next slot flag as CAN_PUT_FLAG.long uid = slots[nextCursorIndex];flags[nextCursorIndex].set(CAN_PUT_FLAG);// Note that: Step 2,3 can not swap. If we set flag before get value of slot, the producer may overwrite the// slot with a new UID, and this may cause the consumer take the UID twice after walk a round the ringreturn uid;}
  1. 通过AtomicLong.updateAndGet来避免对整个方法进行加锁,获取一个可以访问的UID的游标值,根据这个下标获取slots中相关的uid直接返回
  2. 缓存中可用的uid(Tail - Cursor)小于一定阈值的时候,需要启动另外一个线程来生成一批UID

UID 的生成

public synchronized boolean put(long uid) {long currentTail = tail.get();long currentCursor = cursor.get();// tail catches the cursor, means that you can't put any cause of RingBuffer is fulllong distance = currentTail - (currentCursor == START_POINT ? 0 : currentCursor);if (distance == bufferSize - 1) {rejectedPutHandler.rejectPutBuffer(this, uid);return false;}// 1. pre-check whether the flag is CAN_PUT_FLAGint nextTailIndex = calSlotIndex(currentTail + 1);if (flags[nextTailIndex].get() != CAN_PUT_FLAG) {rejectedPutHandler.rejectPutBuffer(this, uid);return false;}// 2. put UID in the next slot// 3. update next slot' flag to CAN_TAKE_FLAG// 4. publish tail with sequence increase by oneslots[nextTailIndex] = uid;flags[nextTailIndex].set(CAN_TAKE_FLAG);tail.incrementAndGet();// The atomicity of operations above, guarantees by 'synchronized'. In another word,// the take operation can't consume the UID we just put, until the tail is published(tail.incrementAndGet())return true;}
  1. 获取Tail的下标值,如果缓存区满的话直接调用RejectedPutHandler.rejectPutBuffer方法
  2. 未满的话将UID放置在slots数组相应的位置上,同时将Flags数组相应的位置改为CAN_TAKE_FLAG

CachedUidGenerator通过缓存的方式预先生成一批UID列表,可以解决UID获取时候的耗时,但这种方式也有不好点,一方面需要耗费内存来缓存这部分数据,另外如果访问量不大的情况下,提前生成的UID中的时间戳可能是很早之前的,DefaultUidGenerator应该在大部分的场景中就可以满足相关的需求了。

作者:liujianhuiouc
链接:https://www.jianshu.com/p/761688b4d6dd
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

UidGenerator相关推荐

  1. 百度开源分布式id生成器uid-generator源码剖析

    百度uid-generator源码 https://github.com/baidu/uid-generator snowflake算法 uid-generator是基于Twitter开源的snowf ...

  2. 搞定全局ID生成器:SpringBoot2.x 集成百度 uidgenerator

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:推荐19个github超牛逼项目!个人原创100W +访问量博客:点击前往,查看更多 作者:风.foxwho fo ...

  3. GDCM:gdcm::UIDGenerator的测试程序

    GDCM:gdcm::UIDGenerator的测试程序 GDCM:gdcm::UIDGenerator的测试程序 GDCM:gdcm::UIDGenerator的测试程序 #include &quo ...

  4. UidGenerator:百度开源的分布式ID服务(解决了时钟回拨问题)

    idGenerator是百度开源的Java语言实现,基于Snowflake算法的唯一ID生成器.而且,它非常适合虚拟环境,比如:Docker.另外,它通过消费未来时间克服了雪花算法的并发限制.UidG ...

  5. 分布式ID-百度(uid-generator)

    github地址:uid-generator uid-generator使用的就是snowflake,只是在生产机器id,也叫做workId时有所不同. uid-generator中的workId是由 ...

  6. 百度开源的分布式唯一ID生成器UidGenerator,解决了时钟回拨问题

    转载自   百度开源的分布式唯一ID生成器UidGenerator,解决了时钟回拨问题 UidGenerator是百度开源的Java语言实现,基于Snowflake算法的唯一ID生成器.而且,它非常适 ...

  7. 融云发送图片消息_IM消息ID技术专题(五):开源分布式ID生成器UidGenerator的技术实现...

    1.引言 很多人一想到IM应用开发,第一印象就是"长连接"."socket"."保活"."协议"这些关键词,没错,这些确 ...

  8. 基于百度的UidGenerator改造的全局唯一id生成器

    其实百度的UidGenerator生成器也是基于snowflake算法的,在原生的项目中使用的基于数据库的,我改造成了基于zookpeer的,用zookpeer的序列号实现分布式seq的生成(代替了算 ...

  9. springboot2.x 集成百度 ui-dgenerator生成分布式唯一id

    百度的ui-dgenerator也是根据snowflake算法更改的方法,关于snowflake算法不做介绍,不懂的可以百度. snowflake算法有个缺点是:时间回拨问题,官方文档也是抛出异常. ...

最新文章

  1. python中正确的表达式_python中如何正确使用正则表达式的详细模式(Verbose mode expression)...
  2. 基于视觉惯性里程计的无监督深度补全方法
  3. 培训第二弹:全国大学生智能汽车竞赛百度竞速组预告
  4. delphi数组问题
  5. ejb jsf jpa_完整的WebApplication JSF EJB JPA JAAS –第2部分
  6. mysql--------四种索引类型
  7. 鸿蒙WLAN模组联网+解决在Visual Studio Code不能更改Linux文件的问题
  8. Linux学习笔记(四)之查看登录用户
  9. 小米鼠标垫功能奇异 价格也不贵
  10. 《小艾上班记》—— 读后总结
  11. sqlserver200864位下载_sql server 2008 r2中文版
  12. 超声波测距(含报警功能)
  13. 日期转换 EEE MMM dd HH:mm:ss zzz yyyy
  14. Macromedia FlashPaper
  15. Python正则表达式及match函数的用法
  16. kali的ip转发失败怎么解决?
  17. outlook默认签名设置_如何将默认签名添加到Outlook会议请求
  18. 发现4个神奇的软件,个个暗藏惊喜,不占内存亲测好评
  19. 串口实现PC之间传输文件
  20. 防范IFEO映像劫持

热门文章

  1. C++命名空间namespace
  2. 【OpenCV3】如何给图像添加(不)透明度通道
  3. 第4章 与缓冲区有关的函数
  4. Css 浏览器兼容性及其其他常见问题
  5. 基于双TMS320C6678+双XC6VSX315T的6U VPX高速数据处理平台
  6. 解决stamp mismatch with graph file
  7. 1024分辨率章子怡/郭富城《最爱》HD国语中字
  8. ADSL获取的IP地址与网关相同,却能上网的原理
  9. VC++实现任务管理器功能
  10. 计算机程序是怎样运行的-hello world程序运行原理