数据传输 | DTLE并行回放性能测试报告

作者:刘安

爱可生测试团队成员,主要负责 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=WRITESETtransaction_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 之间的网络延迟要尽可能的小,这样可以明显提升传输速度

你可能感兴趣的