DM 同步 mysql到 tidb 延迟 波动

【 MySQL 版本】5.7
【 TiDB 版本】5.4.1
【遇到的问题】通过自己的延迟监控和应用测试,发现下游TiDB延迟有波动,延迟有时是0,有时接近1s。
测试环境,SSD,QPS 100左右。
dashboard上有慢日志全是comimt延迟600ms(延迟不稳定和commit慢有关???)。

DM配置
# ----------- 全局配置 -----------
# 任务名称,需要全局唯一
name: “my2tidb_xxxzy”

# 任务模式,可设为 "full"、"incremental"、"all"。all表示全量+增量
task-mode: all    

# 下游储存 `meta` 信息的数据库,同步位点信息存放在表 xxxx_syncer_checkpoint
meta-schema: "dm_meta"         

#是否大小敏感
case-sensitive: false    

#online DDL
online-ddl: true          # 支持上游 "gh-ost" 、"pt" 的自动处理
#online-ddl-scheme: "pt"   # `online-ddl-scheme` 在未来将会被弃用,建议使用 `online-ddl` 代替 `online-ddl-scheme`


# 下游数据库实例配置
target-database:       
  host: "ccc"
  port: 4000
  user: "root"
  password: "zLHo4i9gnUshZyPgIKZvXECpFxInJEA="         # 推荐使用经过 dmctl 加密的密文
  max-allowed-packet: 67108864                         # TiDB默认 67108864 (64 MB)


#黑白名单配置
block-allow-list:                     # 上游数据库实例匹配的表的 block-allow-list 过滤规则集
  bw-rule-1:                          # 黑白名单配置的名称
    do-dbs: ["xxx",]             
      
mydumpers:                           # dump 处理单元的运行配置参数
  global:                            # 配置名称
    #rows: 2000                      # 开启单表多线程并发导出,值为导出的每个 chunk 包含的最大行数。如果设置了 rows,DM 会忽略 chunk-filesize 的值。
    threads: 4                       # dump 处理单元从上游数据库实例导出数据的线程数量,默认值为 4
    chunk-filesize: 64               # dump 处理单元生成的数据文件大小,默认值为 64,单位为 MB
    extra-args: "--consistency none" # dump 处理单元的其他参数,不需要在 extra-args 中配置 table-list,DM 会自动生成
    
loaders:                             # load 处理单元的运行配置参数
  global:                            # 配置名称
    pool-size: 16                    # load 处理单元并发执行 dump 处理单元的 SQL 文件的线程数量,默认值为 16,正常情况下不需要设置。当有多个实例同时向 TiDB 迁移数据时可根据负载情况适当调小该值
    dir: "./dumped_data"             # dump 处理单元输出 SQL 文件的目录,同时也是 load 处理单元读取文件的目录。该配置项的默认值为 "./dumped_data"。同实例对应的不同任务必须配置不同的目录
    
syncers:                             # sync 处理单元的运行配置参数
  global:                            # 配置名称
    worker-count: 16                 # 应用已传输到本地的 binlog 的并发线程数量,默认值为 16。调整此参数不会影响上游拉取日志的并发,但会对下游产生显著压力。
    batch: 200                       # sync 迁移到下游数据库的一个事务批次 SQL 语句数,默认值为 100。
    
    
# ----------- 实例配置 -----------    
mysql-instances:                      
  - source-id: "mysql-xxxzy"               # 上游实例,即MySQL source_id
    block-allow-list:  "bw-rule-1"         # 黑白名单配置名称
    mydumper-config-name: "global"          # mydumpers 配置的名称
    loader-config-name: "global"            # loaders 配置的名称
    syncer-config-name: "global"            # syncers 配置的名称

同步延迟截图:
image

dashboard截图:

麻烦大佬们指点下哪里可以优化,减少延迟不稳定。

workload 是纯插入的吗?监控是用怎么计算延迟的呢?在 DM 的 Prometheus 监控可以看到增量同步阶段的 lag 的,建议看看那个有没有明显的波动。

上游Mysql 各种操作都有,非仅仅插入。

监控计算方式:在上游建一张表(只有一行数据),然后定时更新为当前数据,然后读取上下游的这张表的数据,比较差异并将差异写到结果表。

import os, time,datetime
import pymysql
from apscheduler.schedulers.blocking import BlockingScheduler
scheduler = BlockingScheduler()

'''
上游MySQL建库表

-- 连接数据库
use abc;

-- monitor_time
CREATE TABLE `monitor_time` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `t` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 初始化数据,因后面监控程序定时更新该条记录 
insert into monitor_time(t)  select 1;

-- 创建监控结果表monitor_result;
CREATE TABLE `monitor_result` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `lag_ms` int(11) DEFAULT NULL COMMENT '延迟时间',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '监控记录生成时间',
  PRIMARY KEY (`id`),
  KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
;
--  查看结果
select * from monitor_result;
'''

#定期更新源库表 monitor_time 数据:更新为当前时间
def monitor_time():
        info_src = {'user': "abc", 'passwd': "111", 'host': "xxx.96", 'db': "abc",
                          'port': 12345}
        conn_src = pymysql.connect(user=info_src['user'], passwd=info_src['passwd'],
                                    host=info_src['host'], db=info_src['db'], port=info_src['port'],
                                    charset="utf8")
        t = time.time()
        t1 = int(round(t * 1000))
        print(t1)
        cur = conn_src.cursor()
        cur.execute("update monitor_time set t=%s where 1=1 " ,(str(t1)))
        conn_src.commit()


#定时读monitor_time,比较差异数据,并写入表 monitor_time
def monitor_result():
    info_src = {'user': "abc", 'passwd': "111", 'host': "xxx.96", 'db': "abc", 'port': 12345}
    info_des = {'user': "abc", 'passwd': "111", 'host': "xxx.118", 'db': "abc", 'port': 3390}

    # 3天前的整点
    today = datetime.datetime.now()
    offset = datetime.timedelta(days=-3)
    date_ago = (today + offset).strftime('%Y-%m-%d 00:00:00')
    print(date_ago)

    conn_src = pymysql.connect(user=info_src['user'], passwd=info_src['passwd'],
                               host=info_src['host'], db=info_src['db'], port=info_src['port'],
                               charset="utf8")
    cur_src = conn_src.cursor()

    conn_des = pymysql.connect(user=info_des['user'], passwd=info_des['passwd'],
                               host=info_des['host'], db=info_des['db'], port=info_des['port'],
                               charset="utf8")
    cur_des = conn_des.cursor()

    sql_get_time = "select t from monitor_time "

    cur_src.execute(sql_get_time)
    v_src_tuple = cur_src.fetchone()
    t_src = v_src_tuple[0]

    cur_des.execute(sql_get_time)
    v_des_tuple = cur_des.fetchone()
    t_des = v_des_tuple[0]

    print(t_src, t_des)
    t1 = (t_src  - t_des)
    cur_src.execute("insert into monitor_result(lag_ms) select %s", (str(t1)))
    conn_src.commit()

if __name__ == '__main__':
    scheduler.add_job(monitor_time,  'interval', seconds=1 ,id='job_monitor_time')
    scheduler.add_job(monitor_result, 'interval', seconds=5 ,id='monitor_result')
    scheduler.start()

DM的Prometheus 监控lag几乎是0:

Pictures20220622110650.bmp (2.9 MB)

这个dm同步问题我司也遇到了,这个原因是mysql 和dm不兼容的问题,具体表现在上游mysql做表结构变更时。
mysql变更比如增加一列 删除一列时。dm同步会大幅度抖动。因为mysql的表结构不是立即变更的。他会有延时。等全部结构变更完了才会同步到tidb下游去。这个动作导致tidb会有延时1秒 50秒这种清空。

这个问题不光dm有 cloudcanal 也有这个问题。

我司的解决方法是dm层不做表结构变更的同步。遇到表结构变更。先在tidb层应用表结构变更。然后在mysql上变更表结构。

最后解决了dm引起的抖动问题。

dm不光会引起tidb的抖动。他的同步同样会拖垮上游mysql。你实际应用时会发现这个问题。

1 个赞

我们测试环境 一般表结构变更很少。正常 dm 同步 就有延迟波动。

  1. 在合库合表场景下,等全部表结构变更完了再继续同步过是悲观同步模式的正常表现,因此会造成一些延迟;还是说,您指的延迟是 online-ddl 导致 DML 阻塞产生的延迟呢:https://docs.pingcap.com/zh/tidb-data-migration/v5.3/feature-online-ddl
  2. 能具体说一下把上游 mysql 拖垮的情况吗?

大佬,我的这个问题帮看下哦。看有什么参数调整可以提高写入性能,从而减少同步延迟。

感觉您的监控是实时进行增量校验呢(DM 后续也会有相关的功能)。因为 DM 会把相同主键的变更分给相同的 worker 进行同步,这样就有可能导致某些热点行产生延迟。另外不知道您对于同一行的若干个改动是怎样计算延迟(比如:对于某个主键为 100 的行,短时间内修改了它两次);DM 会将这两个改动合并成一个,然后同步到下游,而不是分别同步两次。不知道这种情况是否能解释延迟波动的问题。

而且看 replication lag 也几乎没看出延迟波动

我们的一个场景是这样的:有个页面,加一个用户,然后页面会自动刷新显示新增的用户。
1、以前用mysql主从+atlas读写分离,加用户操作(更新在主库),自动刷新显示(查询在从库),由于mysql主从延时低,所以这个功能没问题。
2、现在我们把mysql从库改成tidb, mysql写+tidb读+dm同步+atlas读写分离。以上同样的加用户操作,有时候会出现加用户后页面刷新不会显示新用户,而过一会刷新才有结果。所以想到的dm同步延迟的问题。
测试环境都是低峰状态。感觉不像热点问题。

dashboard上拉的报告

这里的“热点”不是说它同一时间承受了太多的负载,而是说您的测试不一定准确,比如(有可能):

  1. 写入进程写入 1000,binlog 同步到 DM
  2. 检查进程从上游拿到当前结果为 1000
  3. 写入进程写入新结果 2000,binlog 到达 DM
  4. 检查进程拿到下游结果为 900,计算 lag 100
  5. DM 合并同步 1000 + 2000 -> 2000 到下游

这里可以去看一下 dm-worker 的 log,看看具体的 SQL 是在什么时候执行的,或许也能看到造成延迟的原因(比如,SQL 执行时间过长或者处理 binlog 的速度太慢)。

具体的延迟其实是可以参考 replication lag 指标的。像您业务上这种复杂的场景或许可以通过 sysbench 这种更加完备的测试工具来进行复现,再看看 replication lag 是否有波动或者持续在高位水平。如果您觉得延迟太大,或者有看到 replication lag 不断升高,可以上调 syncers 配置项的 worker-count 提高导入的并行度,但同时也会对上下游造成更大的压力;不过如果是由于下游 TiDB 的执行延迟过高,调大并行度可能并没有太大影响,所以还是需要确定一下造成具体延迟的原因。

另外附上 DM 各版本的性能测试报告给您参考一下:
https://docs.pingcap.com/zh/tidb-data-migration/v5.3/dm-benchmark-v5.3.0#dm-530-性能测试报告

创建dm task 可以增加线程数

上游是mysql 下游是tidb 开启dm进行实时同步。 上游插入表 然后开发突然alter 表结构 这个变更又很大 dm同步就断了 。从头开始把整张表再同步一边。 上游的数据库mysql这个时候就出现卡顿现象。这种情况在mysqlbinlog一天3t的时候很容易出现。

dm 在大数据量同步时是有问题的 已经出现过好多次生产事故了。真要同步 用阿里开源的cloudcanal 或者datax。

[2022/6/7 17:26]

是的,每个工具都会有坑,毕竟myql与tidb是不一样的数据库

[2022/6/7 17:26]

有步骤吗 发我看看 我学一下

[2022/6/7 17:27]

你是说坑吗

[2022/6/7 17:27]

集团那边都是用的cloudcanal

[2022/6/7 17:30]

我是说你dm修复的步骤 还是说你现在完全就用cloudcanal同步了

[2022/6/7 17:31]

我这边环境比较多,还在用dm,你要用cloudcanal还是dm

[2022/6/7 17:31]

dm 你后来出问题如何处理?

[2022/6/7 17:32]

就是表结构的变更顺序是怎么操作

[2022/6/7 17:32]

自己手动改tidb表结构?在改动mysql的?

[2022/6/7 17:34]

dm出问题上次是把这个表跳过

感谢您的反馈。DM 在同步 DDL 的时候确实会造成 DML 阻塞,DM 同步断了是任务因为错误而暂停了吗

请问 cloudcanal 这个是云产品吗?内网环境可以用吗?

tidb 5版本的 跟dm的兼容性较差