作者:刘安
爱可生测试团队成员,主要负责 DTLE 开源项目相关测试任务,擅长 Python 自动化测试开发。
本文来源:原创投稿
*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
背景:
在 3.21.07.0 之前 DTLE 也支持并行回放,但是需要事务在源端 MySQL 执行时就要多线程并行执行,并且还要处于同一个组提交中。如此苛刻的条件导致虽然在传输任务中配置了目标端的并发数,但是很难真正的触发DTLE并行回放。
当然如果源端 MySQL 配置了 binlog_transaction_dependency_tracking=WRITESET 也可以实现高并行度的回放,但是这个配置的默认值为 COMMIT_ORDER 。
有什么办法在不改变 MySQL 配置的情况下实现DTLE的高并行度回放?
DTLE 在 3.21.07.0 版本开始支持基于 writeset 的增量并行回放(以下简称 MTS ),在 3.21.08.0 版本中又对这个功能进行了进一步的改善。那么就来看一下 DTLE 的这个行功能对传输速度以及系统资源占用的影响有多大吧。
一、环境准备
1. 源端部署 MySQL 实例
配置binlog_transaction_dependency_tracking=COMMIT_ORDER
shell> dbdeployer deploy single 5.7.31 --remote-access % --bind-address 0.0.0.0 -c skip-name-resolve -c binlog_format=ROW -c binlog_row_image=FULL -c log_slave_updates=ON -c binlog_transaction_dependency_tracking=COMMIT_ORDER --gtid --port 3306
2. 目标端部署 MySQL 实例
shell> dbdeployer deploy single 5.7.31 --remote-access % --bind-address 0.0.0.0 -c skip-name-resolve -c binlog_format=ROW -c binlog_row_image=FULL -c log_slave_updates=ON --gtid --port 3306
3. 部署由两个 DTLE 3.21.08.0 组成的集群
4. 部署DTLE监控系统
请参考我的上一篇文章《如何搭建 DTLE 的监控系统》
二、测试步骤
1. 数据准备
i. 源端 MySQL 插入100万行基础数据
shell> sysbench /usr/share/sysbench/oltp_common.lua --mysql-host=10.186.16.109 --mysql-port=3306 --mysql-user=test --mysql-password=test --mysql-db=test --create_secondary=off --table-size=1000000 --tables=1 prepare
ii. 记录源端MySQL Executed_Gtid_Set的值
mysql> show master status\G;
*************************** 1. row ***************************
File: mysql-bin.000002
Position: 189905893
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set: 00003306-0000-0000-0000-000000003306:1-16,
288f4ef1-1b70-11ec-8add-b083fecd2cf3:1-378
1 row in set (0.00 sec)
iii. 将源端的基础数据复制到目标端 MySQL
iv. 源端 MySQL 插入测试数据,共100000个事务、600000个query
shell> sysbench /usr/share/sysbench/oltp_write_only.lua --mysql-host=10.186.16.109 --mysql-port=3306 --mysql-user=test --mysql-password=test --mysql-db=test --report-interval=10 --table-size=1000000 --tables=1 --time=0 --threads=1 --events=100000 run
# 这是一个事务中包含的query示例
shell> cat general.log
2021-09-24T05:50:49.441174Z 12 Query BEGIN
2021-09-24T05:50:49.441240Z 12 Execute UPDATE sbtest1 SET k=k+1 WHERE id=4997
2021-09-24T05:50:49.441361Z 12 Execute UPDATE sbtest1 SET c='03163446517-15844049190-31912517155-76808269330-04050893973-74319634217-49805144545-10801190640-86257363147-68318210832' WHERE id=5002
2021-09-24T05:50:49.441493Z 12 Execute DELETE FROM sbtest1 WHERE id=5029
2021-09-24T05:50:49.441606Z 12 Execute INSERT INTO sbtest1 (id, k, c, pad) VALUES (5029, 5046, '69556849701-65231705332-27842829357-27348055172-35878376643-27881096491-17377489810-14516858565-38927590402-67912505189', '78037141434-67280017469-22218032946-70184714598-49746437373')
2021-09-24T05:50:49.441724Z 12 Execute COMMIT
2. 运行计算QPS的脚本
# coding=utf-8
import time
from datetime import datetime, timedelta
import pymysql
query_1 = "SHOW MASTER STATUS;"
# 已知要传输600000个query
queries = 600000
run_time = datetime.now()
print(f"RUN TIME: {run_time}")
with pymysql.connect(host='10.186.16.117', port=3306, user='test', passwd='test', autocommit=True) as db:
with db.cursor(pymysql.cursors.DictCursor) as cursor:
cursor.execute(query_1)
data = cursor.fetchone()
init = data['Executed_Gtid_Set']
while True:
cursor.execute(query_1)
data = cursor.fetchone()
if data['Executed_Gtid_Set'] != init:
start_time = datetime.now()
print(f"START TIME: {start_time}")
break
time.sleep(1)
equal_times = 0
last_gtid = ''
# 5秒钟MySQL的gitd不再变动,视为传输结束
while equal_times <= 5:
cursor.execute(query_1)
data = cursor.fetchone()
current_gtid = data['Executed_Gtid_Set']
print(f"CURRENT GTID: {current_gtid}")
if current_gtid == last_gtid:
equal_times += 1
else:
equal_times = 0
last_gtid = current_gtid
time.sleep(1)
end_time = datetime.now() - timedelta(seconds=5)
print(f"END TIME: {end_time}")
diff = (end_time - start_time).seconds
print(f'耗时: {diff}')
print(f'QPS: {queries / diff}')
3. 创建不开 MTS 特性的增量任务
- 配置
Gtid="记录的源端MySQL Executed_Gtid_Set的值"
- 配置
UseMySQLDependency=true
job "no-mts" {
datacenters = ["dc1"]
group "Src" {
affinity {
attribute = "${node.unique.name}"
value = "dtle-src-1"
}
task "src" {
driver = "dtle"
config {
Gtid = "00003306-0000-0000-0000-000000003306:1-16, 288f4ef1-1b70-11ec-8add-b083fecd2cf3:1-378"
ReplicateDoDb = [{
TableSchema = "test"
}]
ConnectionConfig = {
Host = "10.186.16.109"
Port = 3306
User = "test"
Password = "test"
}
}
}
}
group "Dest" {
affinity {
attribute = "${node.unique.name}"
value = "dtle-dest-1"
}
task "dest" {
driver = "dtle"
config {
ParallelWorkers = 32
UseMySQLDependency = true
ConnectionConfig = {
Host = "10.186.16.117"
Port = 3306
User = "test"
Password = "test"
}
}
}
}
}
三、其他的使用场景测试
1. MySQL 不启用 MTS,DTLE 启用 MTS 场景
- 需要将前述测试步骤中创建的DTLE任务中的配置改为
UseMySQLDependency=false
2. MySQL 启用 MTS,DTLE 不启用 MTS 场景
需要重新部署源端 MySQL 实例并添加如下配置:
binlog_transaction_dependency_tracking=WRITESET
transaction_write_set_extraction=XXHASH64
shell> dbdeployer deploy single 5.7.31 --remote-access % --bind-address 0.0.0.0 -c skip-name-resolve -c binlog_format=ROW -c binlog_row_image=FULL -c log_slave_updates=ON -c binlog_transaction_dependency_tracking=WRITESET -c transaction_write_set_extraction=XXHASH64 --gtid --port 3306
3. MySQL 和 DTLE 都启用 MTS 场景
4. 以上场景都可以增加目标端 DTLE 到目标端 MySQL 的网络延迟来进一步对比测试
四、测试结果
MySQL配置 | DTLE配置 | 网络延迟 | QPS | cpu-源端 | cpu-目标端 | 内存-源端 | 内存-目标端 | 带宽 |
---|---|---|---|---|---|---|---|---|
MySQL:off | MTS:off | 0ms | 2575 | 58% | 35% | 138MiB | 162MiB | 2.21Mib/s |
MySQL:off | MTS:off | 20ms | 41 | 2% | 2% | 137MiB | 165MiB | 36.5Kib/s |
MySQL:off | MTS:on | 0ms | 6666 | 138% | 70% | 139MiB | 158MiB | 5.94Mib/s |
MySQL:off | MTS:on | 20ms | 1140 | 23% | 14% | 138MiB | 170MiB | 1Mib/s |
MySQL:on | MTS:off | 0ms | 6593 | 135% | 73% | 138MiB | 167MiB | 5.79Mib/s |
MySQL:on | MTS:off | 20ms | 1158 | 23% | 15% | 139MiB | 169MiB | 1Mib/s |
MySQL:on | MTS:on | 0ms | 6976 | 134% | 73% | 139MiB | 171MiB | 6.26Mib/s |
MySQL:on | MTS:on | 20ms | 1145 | 21% | 14% | 139MiB | 167MiB | 1Mib/s |
备注:
- MySQL:off表示MySQL配置
binlog_transaction_dependency_tracking=COMMIT_ORDER
- MySQL:on表示MySQL配置
binlog_transaction_dependency_tracking=WRITESET
、transaction_write_set_extraction=XXHASH64
- MTS:off表示DTLE任务配置
UseMySQLDependency = true
- MTS:on表示DTLE任务配置
UseMySQLDependency = flase
- 网络延迟指的是目标端DTLE到目标端MySQL的网络延迟
五、结论
- MySQL 和 DTLE 中只需有一侧开启 MTS 功能,就可以大幅提升 DTLE 传输速度
- 开启 MTS 功能并且变更的数据不相互依赖时,在无网络延迟时可以比不开 MTS 提高2.6倍的传输速度,在网络延迟为20ms时可以不开 MTS 提高28倍的传输速度
- 开启 MTS 功能,DTLE 对内存的使用并无明显变化,DTLE 对 CPU 的使用有升高了1倍多
- 目标端 DTLE 和目标端的 MySQL 之间的网络延迟要尽可能的小,这样可以明显提升传输速度