分布式ID(CosId)之号段链模式性能(1.2亿/s)解析

上一篇文章《分布式ID生成器(CosId)设计与实现》我们已经简单讨论过CosId的设计与实现全貌。

但是有很多同学有一些疑问:CosId的号段链模式(SegmentChainId)性能有些难以置信(TPS峰值性能1.2亿/s),甚至是不同性能级别号段分发器(RedisIdSegmentDistributorJdbcIdSegmentDistributor)均能够达到这样的性能级别。

所以本篇文章将深度解析CosId的号段链模式(SegmentChainId)的设计思路与实现优化。

新同学最好先查阅上一篇文章《分布式ID生成器(CosId)设计与实现》

背景(为什么需要SegmentChainId

通过上一篇文章《分布式ID生成器(CosId)设计与实现》我们知道号段模式(SegmentId)主要有以下问题:

  • 稳定性:SegmentId的稳定性问题主要是因为号段用完之后获取ID的线程需要同步进行NextMaxId导致的(会产生网络IO)。
  • 步长(Step):设置多大的步长是一个权衡问题,设置太小会影响整体性能,设置太大会导致全局ID乱序的程度增加。
  • 本机单调递增、全局趋势递增: 本地单调递增是我们期望看到的,但是如何理解/衡量全局趋势递增呢。下面我们来解释一下什么是全局趋势递增:

从上图的号段模式设计中我们可以看出:

  • Instance 1每次获取的NextMaxId,一定比上一次大,意味着下一次的号段一定比上一次大,即F(Tn+1)>F(Tn),所以从单实例上来看是单调递增的。
  • Instance 1Instance 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依然是乱序的。

所以不难理解的是影响全局ID乱序的因素有俩个:集群规模、Step大小。集群规模是我们不能控制的,但是Step是可以调节的。

所以SegmentChainId就是在这样的背景下诞生的。

号段链模式(SegmentChainId)的优势

通过SegmentChainId设计图中我们可以看到,号段链模式新增了一个角色PrefetchWorker

PrefetchWorker主要的职责是维护和保证号段链头部到尾部的安全距离,也可以近似理解为缓冲距离。

有了安全距离的保障不难得出的结论是所有获取ID的线程只要从进程内存的号段里边获取下次ID即可,理想情况下不需要再进行NextMaxId(向号段分发器请求NextMaxId,网络IO)的,所以性能可以达到近似AtomicLongTPS 性能:12743W+/s的级别。

SegmentChainIdSegmentId的增强版,相比于SegmentId有以下优势:

  • TPS性能:可达到近似 AtomicLongTPS 性能: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的时候RedisChainIdBenchmarkMySqlChainIdBenchmarkTPS性能几乎一致(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)

  1. <=TPS(AtomicLong):因为SegmentChainId的内部号段就是使用的AtomicLong,所以这是性能上限。
  2. Step*ExpansionExpansion可以理解为饥饿膨胀系数,默认的饥饿膨胀系数是2。在MySqlChainIdBenchmarkMySqlChainIdBenchmark基准测试中这个值是一样的。
  3. TPS(IdSegmentDistributor): 这是公式中唯一的不同。指的是请求号段分发器NextMaxId的TPS。
  4. T: 可以理解为基准测试运行时常。

从上面的公式中不难看出RedisChainIdBenchmarkMySqlChainIdBenchmark主要差异是分发器的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)解析的更多相关文章

  1. CosId 1.1.8 发布,通用、灵活、高性能的分布式 ID 生成器

    CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式 ID 生成器. 目前提供了三类 ID 生成器: SnowflakeId : 单机 TPS 性 ...

  2. 分布式ID生成器(CosId)的设计与实现

    分布式ID生成器(CosId)设计与实现 CosId 简介 CosId 旨在提供通用.灵活.高性能的分布式 ID 生成器. 目前提供了俩类 ID 生成器: SnowflakeId : 单机 TPS 性 ...

  3. 大型互联网公司分布式ID方案总结

    ID是数据的唯一标识,传统的做法是利用UUID和数据库的自增ID,在互联网企业中,大部分公司使用的都是Mysql,并且因为需要事务支持,所以通常会使用Innodb存储引擎,UUID太长以及无序,所以并 ...

  4. 分布式id生成方案总结

    本文已经收录自 JavaGuide (60k+ Star[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.) 本文授权转载自:https://juejin.im/post/ ...

  5. 面试总被问分布式ID怎么办? 滴滴(Tinyid)甩给他

    整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 一口气说出 9种 分布式ID生成方式,面试官有点懵了 面试总被问 ...

  6. 分布式ID

    需求 全局唯一 高性能 高可用 简单易用 UUID 优点: 唯一 不依赖于任何第三方服务 缺点: 是字符串类型而非数字,不满足数字ID的需求 字符串太长了,DB查询效率受影响 数据库自增ID 如果使用 ...

  7. 触发链模式之使用jdk的Observable和Observerver实现触发链模式(附JDK源码)

    首先看看JDK的Observer接口 public interface Observer { void update(Observable o, Object arg); } 也就一个更新的方法,这里 ...

  8. CosId 1.1.0 发布,通用、灵活、高性能的分布式 ID 生成器

    CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式系统 ID 生成器. 目前提供了俩大类 ID 生成器:SnowflakeId (单机 TPS ...

  9. CosId 1.0.3 发布,通用、灵活、高性能的分布式 ID 生成器

    CosId 通用.灵活.高性能的分布式 ID 生成器 介绍 CosId 旨在提供通用.灵活.高性能的分布式系统 ID 生成器. 目前提供了俩大类 ID 生成器:SnowflakeId (单机 TPS ...

随机推荐

  1. SpringBoot数据访问(三) SpringBoot整合Redis

    前言 除了对关系型数据库的整合支持外,SpringBoot对非关系型数据库也提供了非常好的支持,比如,对Redis的支持. Redis(Remote Dictionary Server,即远程字典服务 ...

  2. 31、服务器磁盘、内存、cpu使用率监控

    31.1.监控磁盘: #!/bin/sh diskspace="`df -hT`" IFS="\n" disk_value="80" ech ...

  3. frp+nginx内网穿透

    frp+nginx内网穿透 背景:自己有台内网Linux主机,希望被外网访问(ssh.http.https): 准备工作 内网Linux主机-c,可以访问c主机和外网的主机-s(windows/lin ...

  4. Linux基础 -01

    01Linux快速入门 1.计算机组成原理 1.1什么是计算机 计算机一般被称为"电脑",即通电的大脑 电脑二字蕴含了人类对计算机的终极期望; 希望它能像人脑一样为我们工作,从而取 ...

  5. SpringMVC(4)数据绑定-1

    在SpringMVC(3)URL请求到Action的映射规则我们介绍了请求是如何映射到一个action上的,下一步当然是如何获取到请求中的数据,这就引出了本篇所要讲的内容-数据绑定. 首先看一下都有哪 ...

  6. Linux查找占用的端口,并杀死进程

    我要使用4040端口,但是被其他的程序占用了 1. 查找占用的程序 netstat -apn | grep 4040 最后一项显示的是pid和对应的名称 2. 杀掉对应的进程,彻底杀死进程 kill ...

  7. 根据使用者反馈,对开源项目 go-gin-api 新增两个功能

    目录 前言 接口返回的错误信息支持中英文 代码位置 使用方式 错误信息自定义 参数验证的错误信息支持中英文 代码位置 使用方式 错误信息语言包 示例 小结 推荐阅读 前言 根据使用者的反馈,对开源项目 ...

  8. Docker部署Mysq集群

    1.PXC(Percona XtraDB Cluster) 速度慢 但能保证强一致性 适用于保存价值较高的数据 数据同步是双向的 在任一节点写入数据 都会同步到其他所有节点 在任何节点上都能同时读写 ...

  9. 二进制方式搭建Kubernetes集群

    环境准备: 演练暂时用单节点一台master和一台node节点来进行部署搭建(kubernetes 1.19版本) 角色 IP 组件 master 10.129.246.114 kube-apiser ...

  10. CTF反序列化逃逸

    刷了一下CTF反序列化的题,去年没有好好了解.又补了一次PHP,害太菜了.每天看看别人的博客真的可以鼓舞人.简单记录一下两道字符串逃逸问题 推荐一个反序列化总结的很好的笔记https://www.cn ...