分布式ID(CosId)之号段链模式性能(1.2亿/s)解析
分布式ID(CosId)之号段链模式性能(1.2亿/s)解析
上一篇文章《分布式ID生成器(CosId)设计与实现》我们已经简单讨论过CosId的设计与实现全貌。
但是有很多同学有一些疑问:CosId的号段链模式(SegmentChainId)性能有些难以置信(TPS峰值性能1.2亿/s),甚至是不同性能级别号段分发器(RedisIdSegmentDistributor、JdbcIdSegmentDistributor)均能够达到这样的性能级别。
所以本篇文章将深度解析CosId的号段链模式(SegmentChainId)的设计思路与实现优化。
新同学最好先查阅上一篇文章《分布式ID生成器(CosId)设计与实现》。
背景(为什么需要SegmentChainId)
通过上一篇文章《分布式ID生成器(CosId)设计与实现》我们知道号段模式(SegmentId)主要有以下问题:
- 稳定性:SegmentId的稳定性问题主要是因为号段用完之后获取ID的线程需要同步进行
NextMaxId
导致的(会产生网络IO)。 - 步长(Step):设置多大的步长是一个权衡问题,设置太小会影响整体性能,设置太大会导致全局ID乱序的程度增加。
- 本机单调递增、全局趋势递增: 本地单调递增是我们期望看到的,但是如何理解/衡量全局趋势递增呢。下面我们来解释一下什么是全局趋势递增:
从上图的号段模式设计中我们可以看出:
- Instance 1每次获取的
NextMaxId
,一定比上一次大,意味着下一次的号段一定比上一次大,即F(Tn+1)>F(Tn),所以从单实例上来看是单调递增的。 - Instance 1、Instance 2实例各自持有的不同的号段,意味着在一个时间段内不同实例生成的ID是乱序的。但是多实例在获取
NextMaxId
时是单调递增的,所以整体趋势的递增,即全局趋势递增。
全局趋势递增反向说明的是ID在一个时间周期内是会乱序的,所以我们要尽可能让ID的乱序程度降低。这是一个优化点。
号段模式生成的ID,在数据库视角可以近似的理解为上面的趋势递增图(Tn>Tn-s)。ID乱序的程度受到步长(Step)影响,步长越小ID乱序的程度越小。这里我们使用边界值(Step=1
)作一下说明。
- 当
Step=1
时,即每次都获取NextMaxId
,将无限接近单调递增(数据库视角)。需要注意的是这里是无限接近而非等于单调递增,具体原因你可以思考一下这样一个场景:- 号段分发器T1时刻给Instance 1分发了
ID=1
,T2时刻给Instance 2分发了ID=2
。因为机器性能、网络等原因,Instance 2
网络IO写请求先于Instance 1
到达。那么这个时候对于数据库来说,ID依然是乱序的。
- 号段分发器T1时刻给Instance 1分发了
所以不难理解的是影响全局ID乱序的因素有俩个:集群规模、Step
大小。集群规模是我们不能控制的,但是Step
是可以调节的。
所以SegmentChainId就是在这样的背景下诞生的。
号段链模式(SegmentChainId)的优势
通过SegmentChainId设计图中我们可以看到,号段链模式新增了一个角色PrefetchWorker。
PrefetchWorker主要的职责是维护和保证号段链头部到尾部的安全距离,也可以近似理解为缓冲距离。
有了安全距离的保障不难得出的结论是所有获取ID的线程只要从进程内存的号段里边获取下次ID即可,理想情况下不需要再进行NextMaxId
(向号段分发器请求NextMaxId
,网络IO)的,所以性能可以达到近似AtomicLong
的 TPS 性能:12743W+/s的级别。
SegmentChainId是SegmentId的增强版,相比于SegmentId有以下优势:
- TPS性能:可达到近似
AtomicLong
的 TPS 性能:12743W+/s JMH 基准测试。通过引入了新的角色PrefetchWorker用以维护和保证安全距离,理想情况下使得获取ID的线程几乎完全不需要进行同步的等待NextMaxId
获取。 - 稳定性:P9999=0.208(us/op),通过上面的TPS性能描述中我们可以看到,SegmentChainId消除了同步等待的问题,所以稳定性问题也因此迎刃而解。
- 适应性:从SegmentId介绍中我们知道了影响ID乱序的因素有俩个:集群规模、
Step
大小。集群规模是我们不能控制的,但是Step
是可以调节的。Step
应该尽可能小才能使得ID单调递增的可能性增大。Step
太小会影响吞吐量,那么我们如何合理设置Step
呢?答案是我们无法准确预估所有时点的吞吐量需求,那么最好的办法是吞吐量需求高时,Step自动增大,吞吐量低时Step自动收缩。- SegmentChainId引入了饥饿状态的概念,PrefetchWorker会根据饥饿状态检测当前安全距离是否需要膨胀或者收缩,以便获得吞吐量与有序性之间的权衡,这便是SegmentChainId的自适应性。
- 所以在使用SegmentChainId时我们可以配置一个比较小的
Step
步长,然后由PrefetchWorker根据吞吐量需求自动调节安全距离,来自动伸缩步长。
常见问题
RedisIdSegmentDistributor、JdbcIdSegmentDistributor 均能够达到TPS=1.2亿/s?
RedisChainIdBenchmark-Throughput
MySqlChainIdBenchmark-Throughput
上面的两张图给许多同学带来了困扰,为什么在Step=1000
的时候RedisChainIdBenchmark、MySqlChainIdBenchmarkTPS性能几乎一致(TPS=1.2亿/s)。
RedisIdSegmentDistributor应该要比JdbcIdSegmentDistributor性能更高才对啊,为什么都能达到AtomicLong性能上限呢?
如果我说当Step=1
时,只要基准测试的时间够长,那么他们依然能够达到AtomicLong性能级别(TPS=1.2亿/s),你会不会更加困惑。
其实这里的障眼法是PrefetchWorker的饥饿膨胀导致的,SegmentChainId的极限性能跟分发器的TPS性能没有直接关系,因为最终都可以因饥饿膨胀到性能上限,只要给足够的时间膨胀。
而为什么在上图的Step=1
时TPS差异还是很明显的,这是因为RedisIdSegmentDistributor膨胀得更快,而基准测试又没有给足测试时间而已。
SegmentChainId基准测试TPS极限性能可以近似使用以下的公式的表示:
TPS(SegmentChainId)极限值=(Step*Expansion)*TPS(IdSegmentDistributor)*T/s<=TPS(AtomicLong)
<=TPS(AtomicLong)
:因为SegmentChainId的内部号段就是使用的AtomicLong
,所以这是性能上限。Step*Expansion
:Expansion可以理解为饥饿膨胀系数,默认的饥饿膨胀系数是2。在MySqlChainIdBenchmark、MySqlChainIdBenchmark基准测试中这个值是一样的。TPS(IdSegmentDistributor)
: 这是公式中唯一的不同。指的是请求号段分发器NextMaxId
的TPS。T
: 可以理解为基准测试运行时常。
从上面的公式中不难看出RedisChainIdBenchmark、MySqlChainIdBenchmark主要差异是分发器的TPS性能。
分发器的TPS(IdSegmentDistributor)
越大,达到TPS(AtomicLong)
所需的T
就越少。但只要T
足够长,那么任何分发器都可以达到近似TPS(AtomicLong)
。
这也就解释了为什么不同TPS性能级别的号段分发器(IdSegmentDistributor)都可以达到TPS=1.2亿/s。
CosId需要部署服务端吗?
CosId是以本地SDK的形式存在的,用户只需要安装一下CosId的依赖包做一些简单配置(快速开始、DEMO)即可。
分布式ID是不适合使用服务端部署模式的(C/S)。使用服务端部署模式,必然会产生网络IO(Client通过远程过程调用Server,获取ID),你想想我们费了那么大劲消除网络IO是为了什么?
PrefetchWorker 是如何维护安全距离的?
- 定时维护:每隔一段时间PrefetchWorker会主动检测安全距离是否满足配置要求,如果不满足则执行
NextMaxId
预取,保证安全距离。 - 被动饥饿唤醒:当获取ID的线程获取ID时没有可用号段,会尝试获取新的号段,并主动唤醒PrefetchWorker并告诉他你太慢了,被唤醒的PrefetchWorker会检测安全距离是否需要膨胀,然后进行安全距离的维护。
本机单调、全局趋势递增-为什么还要尽可能保证单调递增?
从上文的论述中我们不难理解本机单调递增,全局趋势递增是权衡后的设计结果。
但是全局趋势递增的背面是周期内ID乱序,所以尽可能向单调递增优化(降低ID乱序程度)是优化目标,这俩点并不冲突。
如果各位同学还有其他问题请至 Issues 提交你的疑问。
分布式ID(CosId)之号段链模式性能(1.2亿/s)解析的更多相关文章
- CosId 1.1.8 发布,通用、灵活、高性能的分布式 ID 生成器
CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式 ID 生成器. 目前提供了三类 ID 生成器: SnowflakeId : 单机 TPS 性 ...
- 分布式ID生成器(CosId)的设计与实现
分布式ID生成器(CosId)设计与实现 CosId 简介 CosId 旨在提供通用.灵活.高性能的分布式 ID 生成器. 目前提供了俩类 ID 生成器: SnowflakeId : 单机 TPS 性 ...
- 大型互联网公司分布式ID方案总结
ID是数据的唯一标识,传统的做法是利用UUID和数据库的自增ID,在互联网企业中,大部分公司使用的都是Mysql,并且因为需要事务支持,所以通常会使用Innodb存储引擎,UUID太长以及无序,所以并 ...
- 分布式id生成方案总结
本文已经收录自 JavaGuide (60k+ Star[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.) 本文授权转载自:https://juejin.im/post/ ...
- 面试总被问分布式ID怎么办? 滴滴(Tinyid)甩给他
整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 一口气说出 9种 分布式ID生成方式,面试官有点懵了 面试总被问 ...
- 分布式ID
需求 全局唯一 高性能 高可用 简单易用 UUID 优点: 唯一 不依赖于任何第三方服务 缺点: 是字符串类型而非数字,不满足数字ID的需求 字符串太长了,DB查询效率受影响 数据库自增ID 如果使用 ...
- 触发链模式之使用jdk的Observable和Observerver实现触发链模式(附JDK源码)
首先看看JDK的Observer接口 public interface Observer { void update(Observable o, Object arg); } 也就一个更新的方法,这里 ...
- CosId 1.1.0 发布,通用、灵活、高性能的分布式 ID 生成器
CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式系统 ID 生成器. 目前提供了俩大类 ID 生成器:SnowflakeId (单机 TPS ...
- CosId 1.0.3 发布,通用、灵活、高性能的分布式 ID 生成器
CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式系统 ID 生成器. 目前提供了俩大类 ID 生成器:SnowflakeId (单机 TPS ...
随机推荐
- 《手把手教你》系列基础篇之(四)-java+ selenium自动化测试- 启动三大浏览器(下)基于Maven(详细教程)
1.简介 上一篇文章,宏哥已经在搭建的java项目环境中实践了,今天就在基于maven项目的环境中给小伙伴们 或者童鞋们演示一下. 2.eclipse中新建maven项目 1.依次点击eclipse的 ...
- 为了提高开发效率,我实现了 uTools 的超级面板
前言 为了进一步提高开发工作效率,最近我们基于 electron 开发了一款媲美 uTools 的开源工具箱 rubick.该工具箱不仅仅开源,最重要的是可以使用 uTools 生态内所有开源插件!这 ...
- 【重学Java】可变参数
可变参数 可变参数[应用] 可变参数介绍 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了 方法的参数类型已经确定,个数不确定,我们可以使用可变参数 可变参数定义格式 修饰符 ...
- WebSocket实现实时聊天系统
WebSocket实现实时聊天系统 等闲变却故人心,却道故人心易变. 简介:前几天看了WebSocket,今天体验下它的实时聊天. 一.项目介绍 WebSocket 实时聊天系统自己一个一码的搞出来还 ...
- spring boot项目的maven库查询地址
阿里巴巴地址 http://maven.aliyun.com/nexus/#welcome maven通用地址 http://mvnrepository.com/ gradle默认mavenCentr ...
- 关于mysql binlog二进制
binlog 在mysql中,当发生数据变更时,都会将变更数据的语句,通过二进制形式,存储到binlog日志文件中. 通过binlog文件,你可以查看mysql一段时间内,对数据库的所有改动. 也可以 ...
- error more than one devices and emulator
问题秒速 莫名的多了一个设备,然后再输入adb shell 解决方法: 1.如果确实有多种设备,要指定设备号 adb -s 设备号 shell(设备号在这里是 emulator-5554,其他同理) ...
- Java 内存泄漏知多少?
先看再点赞,给自己一点思考的时间,如果对自己有帮助,微信搜索[程序职场]关注这个执着的职场程序员.我有什么:职场规划指导,技能提升方法,讲不完的职场故事,个人成长经验. 面试的时候内存管理是不是很多面 ...
- 42. Trapping Rain Water [dp][stack]
description: Given n non-negative integers representing an elevation map where the width of each bar ...
- ZooKeeper 分布式锁 Curator 源码 01:可重入锁
前言 一般工作中常用的分布式锁,就是基于 Redis 和 ZooKeeper,前面已经介绍完了 Redisson 锁相关的源码,下面一起看看基于 ZooKeeper 的锁.也就是 Curator 这个 ...