Relay log 类似 binary log,是指一组包含数据库变更事件的文件,加上相关的 index 和 mata 文件,具体细节参考 官方文档 。在 DM 中针对某个上游开启 relay log 后,相比不开启,有如下优势:
但在 DM 版本 <= v2.0.7 中,开启 relay log 后有如下问题:

由于以上问题,在新的版本中,我们对 DM 的 relay log 做了一些性能优化。
在开始介绍具体的优化之前,首先简单介绍下当前 DM 中 relay 的实现,详细实现,详情可参阅 DM 源码阅读系列文章(六)relay log 的实现 ,本文在此不做过多描述。
当前 relay 模块可以分为两个部分,relay writer 和 relay reader 部分,其结构如下所示:

relay writer 依次做了下述 3 个事情:
使用 binlog reader 从上游 MySQL/MariaDB 读取 binlog event;
将读取到的 binlog event 使用 binlog transformer 进行转换;
将转换后的 binlog event 使用 binlog writer 以 relay log file 的形式存储在本地的 relay directory 中。
开启 relay 后,Syncer 会通过 relay reader 获取 binlog event,relay reader 主要是做了如下工作:
从上面的介绍可以看出,relay reader 跟 relay writer 是相互独立的,彼此通过 relay directory 中的 binlog、meta 和 index 文件进行交互。
在开始介绍优化内容前,先介绍下优化时使用的环境情况
上游为 MySQL,版本为
5.7.35-log;
下游为单实例的 TiDB,版本为
5.7.25-TiDB-v5.2.1;
DM 使用了 1 个 master 和 1 个 worker
在小规模测试(<= 4 task)下 MySQL/TiDB/DM 运行在同一主机,方便测试,主机环境为 8 核 16G;
较大规模测试(> 4 task)下 MySQL/TiDB/DM 分别运行在一台主机上,主机都是 8 核 16G
测量迁移延迟采用下游自更新时间列的方式,详细参考 多种方式告诉你如何计算DM同步数据到 TiDB 的延时时间 中的第三种方式。
从上面的“ 当前 relay 实现”部分可以看出,有两个可能影响 latency 的点:
经过调研以及对上述问题的测试,我们总结了两个方案:
方案 1 需要根据下游的写入速度在读内存和读文件之间进行切换,实现起来相对复杂,而且由于 OS 层 page cache 的存在,应用本身再增加一层缓存对 latency 的影响有限。
方案 2,我们做了一些初步的测试,在增加 relay reader check 的频率时,开启 relay 基本能达到不开启 relay 时的latency,调研了下 MySQL 的 relay log,发现也是通过读取文件的方式,因此我们选择了方案 2。
实现相对较简单,在 relay writer 增加了 Listener,在有新 binlog event 时通知该 Listener(往 channel 中发送一个消息),然后在 relay reader 中,将 定时 check 改为监听 channel 中的消息。
下图是在 4 table 4 task 测试下的 latency 结果,可以看出开启 relay 后的 latency 跟不开启很接近:

Latency 优化完后,我们也对开启 relay log 后的 CPU 占用情况做了测试,发现开启后 CPU 占用率也较高,下图是在 4 table 4 task 下测试结果( 注:后续没有特殊说明的话,都是在该场景下的测试结果),可以看出开启 relay 后 CPU 消耗有明显增长,而且尖刺变得更大:

使用 golang 自带的 pprof 做了一个 CPU profile,从下图可以看出占比较大的主要是 syncer/relay reader/relay writer 等部分,对比代码逻辑后,发现:

针对上面的问题,我们做了如下优化:
从下图可以看出优化后 CPU 有较大幅度的降低,但是尖刺仍然较大:

由于我们测试用的 sysbench 产生 write 事件的速率是比较平稳的,DM 中也没有特别的执行代码,而 Golang 是一个编译型带 GC 的语言,因此我们猜测尖刺主要来自于 GC,但是这一点从 CPU profile 并不明显,见下图:

使用
GODEBUG=gctrace=1开启 GC 日志,见下图,可以发现:

下图是上面优化后做的 heap profile 中 alloc_space 部分的火焰图:
说明:pprof 的 heap profile 是程序运行至今的一个剖析,并不是某一段时间内,所以从下图中也可以看到一些驻留内存的申请。

通过 heap profile 并对比代码,发现了以下可优化的点:
优化后的结果如下,可以看出 CPU 又降低了不少,尖刺也少了很多:

下图为在 20 table 20 task 场景下的测试结果:

经过上面的优化,开启 relay 相比不开 relay,在 latency 上的差距已经很小, CPU 的增长也在一个相对低的水平,但是仍有一些点是可以继续优化的,预期会在后续版本中逐步添加,如下:
go-mysql读文件使用的
io.CopyN,这个函数内部会申请一个小对象,高频使用情况下还是对 GC 有一些影响的,但不大,这次暂时没改;no relay和
relay同时生效的优化这次没做,比如 streamer 读取 event 时创建的 timeout context;
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69994146/viewspace-2861801/ ,如需转载,请注明出处,否则将追究法律责任。