多级多路归并
在上篇文章中,咱们通过一系列的测试,针对全局 binlog 的同步能力,得出了几个外围论断 BPS 能够达到 500M+/s EPS 能够达到 220w+/s * TPS 能够达到 35w+/s
测试实例的的规格为:8CN + 8DN + 2CDC 单 CN 节点规格:32 核 128GB 单 DN 节点规格:32 核 128GB * 单 CDC 节点规格:16 核 32GB
此处咱们将 DN 节点的数量由 8 个调整为 16 个,进行复测,看看性能是否仍然能够达到如上规范。以 TPS 为例,应用上篇中的 48 张表的 sysbench 数据导入进行测试
sysbench --config-file='sysb.conf' --create-table-options='dbpartition by hash(`id`) tbpartition by hash(id) tbpartitions 8' --tables='48' --threads='48' --table-size='10000000' oltp_point_select prepare
测试论断
TPS 只能达到 19w+/ s 的程度,链路呈现比拟高的提早,如下图所示:
并且此时多路归并线程曾经满负荷运行,如下图所示:
并且此时多路归并线程曾经满负荷运行,如下图所示:
这是因为零碎默认只开启了单级归并,如下图所示:
随着 DN 数量的增多,参加归并排序的队列长度也会随之增大,性能则相应呈现衰减(另附多路归并外围代码:LogEventMerger.java),解决办法是开启多级归并,通过“分治”和“并发”来解决性能瓶颈,如下所示:
开启多级归并后,吞吐能力复原至失常程度,如下所示:
如上是多级归并的逻辑示意图,反映到理论的运行时拓扑,多级归并能够是线程级的,如下所示:
也能够是过程级的,如下所示:
前者用来解决较小规模集群 (<=64DN) 的性能瓶颈,后者用来解决更大规模集群 (>64DN) 的性能瓶颈。当 DN 数量不太多时(如 32 个),优先思考通过晋升单个节点的配置,采纳线程级别的多级归并来晋升性能,防止不必要的网络传输开销;当 DN 数量十分多时(如 256 个),很难靠单个节点承当计算、内存或网络资源的压力,此时能够通过过程级别的多级归并来晋升性能。当然,多级归并也不是“银弹”,全局 Binlog 会有一个 Global Merge Point,当 DN 数量足够多时,依然会有单点瓶颈问题(尤其是网络瓶颈),能够通过多流 Binlog 进行解决,咱们的后续文章会对 PolarDB- X 的多流 Binlog 进行介绍。
上面表格是 oltp_insert 场景下的测试数据,分隔行以上的局部是单级归并可撑持的压力范畴,当 QPS 高于 20w 之后,会呈现显著的提早,开启多级归并后,QPS 达到 35w 时,延迟时间依然放弃在 1s 以内
流水线 & 并行
全局 Binlog 数据流生产线,采纳了相似 SEDA(Staged Event-Driven Architecture)的架构,共划分为 6 个 Stage,相邻的 Stage 之间以及 Stage 外部组件之间,通过队列进行串联,每个 Stage 外部通过异步或多线程并行技术对数据处理进行减速。
Extract Stage Extract Stage 用于实现一级排序,波及 binlog 解析、数据整形、元数据保护、ddl 解决等,每个 DN 对应一个解决线程,单个 DN 最大可反对的 EPS 吞吐为 25w/s,采纳的性能优化伎俩次要在内存治理和缓存治理层面,见下文的内存应用优化章节。
Merge Stage Merge Stage 用于实现全局排序,保障全局 Binlog 中数据操作的线性统一,采纳的性能优化伎俩次要是多级归并,上文曾经有详细描述,此处不再赘述。
Collect Stage Collect Stage 用于实现事务的合并,将分布式事务的扩散到各个 DN 的 binlog 数据进行排序和合并,采纳的性能优化伎俩次要有:通过 RingBuffer,进行并行处理,大大晋升合并速度 反对“预序列化”,某个事务合并实现之后,如果事务大小小于设定阈值(如果太大,预序列化可能会占用大量内存),会间接构建 Transmit Message 并序列化,为 Transmit Stage 加重压力(Transmit Stage 序列化操作是单线程解决)
Transmit Stage Transmit Stage 用于实现 Task 和 Dumper 之间的网络传输,应用 Grpc 构建 Data Stream,采纳的性能优化伎俩次要有:Batch 模式,多个事务的 event 封装为一个数据包,晋升吞吐率 Single 模式,自动检测大事务,单个事务独立封装数据包,防止内存溢出 异步解决,耗时的序列化和反序列化操作放到独立线程 动静反压管制,防止内存溢出。
Write Stage Write Stage 处于数据流的末端,用来构建终态的全局 Binlog 文件,次要采纳了 3 种优化伎俩:并行构建 应用 RingBuffer 构建缓冲区,将构建 binlog event 的操作 (创立 event、更新 event 内容、计算 CRC32 校验值等) 并行化解决,解决单线程瓶颈。间接内存 采纳间接内存进行 IO 操作,减小用户态和内核态上下文切换 * Write Cache 引入写缓存,并保障缓存大小为 page 页的整数倍,升高 IO 频率和晋升 page 命中率。
Backup Stage Backup Stage 用于实现全局 Binlog 文件的备份,反对 Dumper 之间的主备复制,和到中心化存储 (如 OSS) 的冷备份,次要的性能优化伎俩是多线程上传和下载。
内存应用优化
全局 Binlog 零碎是一个数据密集型的零碎,对内存资源的治理是一个外围关键问题,上面介绍全局 Binlog 在内存优化层面的优化伎俩。
事务数据长久化 排序是全局 Binlog 零碎最外围的性能,在对数据进行解决时,须要将某个事务的 binlog 数据全副接管完之后,能力进行排序操作,当遇到大事务或者超长事务空洞时,如果没有数据的长久化机制,很容易把内存撑爆,导致 full gc 或内存溢出,影响链路的失常运行。对此零碎设计了反对内存和磁盘混合存储的 Hybrid KV Store,用来解决内存不足的问题,数据链路的几个外围 Stage 深度依赖该 Store,如下所示
KV Store 的次要特点有:反对大事务 swap 到磁盘,动静监测事务大小,超过指定阈值后,该事务数据 swap 到磁盘 反对大事件 swap 到磁盘,遇到超过指定阈值的 binlog event,间接保留到磁盘 反对动静监测内存使用率,超过指定阈值后,主动将存量数据和新增数据 swap 到磁盘 全磁盘存储相比纯内存存储,性能大略有 20% 的升高。
元数据长久化 元数据是全局 Binlog 零碎的外围命根子,在库表十分多的状况下,元数据会耗费大量内存,能够多达 10 几 GB,影响数据链路的失常运行。举例来说,48w 张数据表占用了靠近 8G 的内存,如下所示:
零碎提供了针对元数据的长久化机制,开启长久化能力之后,元数据会被序列化并保留至 RocksDB,并反对在内存和磁盘之间的动静 Swap,波及元数据长久化的参数为:meta.persist.basePath : 配置元数据长久化目录 meta.persist.schemaObject.switch : 是否开启元数据长久化。
接下面例子,当开启元数据长久化之后,内存占用大幅升高到只有 240M,如下所示:
优化内存调配 尽管 KV Store 提供了 swap 机制,保障了内存不够用时数据能够转储到磁盘,但毕竟会对性能造成影响,最优的策略还是尽量减少内存的占用,零碎提供的次要优化伎俩有:不同 stage 对内存的要求不一样,正当管制队列缓冲区的大小,躲避不必要的数据沉积 躲避不必要的数据复制,如针对 ByteString 的应用 ByteString.copyFrom 办法能够替换为 UnsafeByteOperations.unsafeWrap 办法,免去字节数组 copy 操作 ByteString.toByteArray 办法能够替换为 DirectByteOutput.unsafeFetch 办法,免去字节数组 copy 操作
总结
本篇从技术原理的角度,对全局 Binlog 的一些外围的性能优化伎俩做了简要的介绍,通过 SEDA 流水线架构实现了异步和并行处理,通过多级归并解决了集群规模扩张时的性能衰减问题,通过一系列的内存优化伎俩保障了数据链路的高效运行,这些优化措施,保障了全局 Binlog 在 TPCC 150w tpmC 的压力下,提早仍能够管制在 1s 以内(参见: 性能白皮书)
当然,全局 Binlog 在架构上也有人造的劣势,其在应答超大规模集群时存在单点瓶颈,PolarDB- X 的多流 Binlog 通过就义事务的完整性解决了单点问题,在应答超大规模集群时,可能保障性能的线性晋升,敬请关注咱们的系列文章。
原文链接
本文为阿里云原创内容,未经容许不得转载。