如何通过mysql binlog排查数据同步问题?

1、需求:

通过binlog把mysql数据从A库(一张表)形变后(根据rec_dt字段确定B库中的表)同步到B库(12张表按月分表)

即:假设记录的rec_dt="2024-07-06 10:17:12:992" 则同步到7月的表 XXX_07的表。

2、出现的问题

每次重启同步工具可以正常同步,此时新增数据A无法同步,然后同步工具挂了,再次重启A可以正常同步,此时新增数据B无法同步,然后同步工具挂了。

3、初步定位问题

查看同步工具,错误原因为lua脚本中分表日期字段rec_dt(timestamp类型)为nil,查看了库里面所有的数据都有值,百思不得其解。

后面在lua脚本中把rec_dt字段做了处理,为空时设置默认值,数据同步正常,增量数据也能正常同步。

4、继续深挖定位问题

这肯定不能放过呀,这么诡异,到底是什么原因呢?于是继续排查。

4.1、查看binlog日志

查看binlog文件:

复制代码
mysql> show binary logs;
+---------------------------------+-----------+
| Log_name                        | File_size |
+---------------------------------+-----------+
| f266a8f5-binlog.000001          |     40067 |
| f266a8f5-binlog.000002          |       217 |
| f266a8f5-binlog.000003          |       217 |
| f266a8f5-binlog.000004          |     30246 |
+---------------------------------+-----------+
4 rows in set (0.01 sec)
sql 复制代码
mysql> show variables like 'log_bin%';
+---------------------------------+----------------------------------------------+
| Variable_name                   | Value                                        |
+---------------------------------+----------------------------------------------+
| log_bin                         | ON                                           |
| log_bin_basename                | /LOG/BIN/f266a8f5-binlog                |
| log_bin_index                   | /LOG/BIN/f266a8f5-binlog.index          |
| log_bin_trust_function_creators | ON                                           |
| log_bin_use_v1_row_events       | OFF                                          |
+---------------------------------+----------------------------------------------+
5 rows in set (0.00 sec)

日志存储的位置:/LOG/BIN/f266a8f5-binlog

2、通过mysqlbinlog远程查看binlog日志。

sql 复制代码
mysqlbinlog  --base64-output=DECODE-PORS    
--read-from-remote-server 
--host=XXX --port=XXX 
--user=XXX --password=XXXX  
f266a8f5-binlog.000004 
sql 复制代码
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c7a8d514-e0a6-11ee-9fe0-b22ac778b3fb:116'/*!*/;
# at 26215
#240704  2:55:27 server id 2887104926  end_log_pos 26302 CRC32 0x476988e3       Query   thread_id=5689417       exec_time=0     error_code=0
SET TIMESTAMP=1720032927.826239/*!*/;
BEGIN
/*!*/;
# at 26302
#240704  2:55:27 server id 2887104926  end_log_pos 26458 CRC32 0x2bc647f9       Table_map: `testdb`.`test_result_log` mapped to number 12120
# at 26458
#240704  2:55:27 server id 2887104926  end_log_pos 26528 CRC32 0xd95f8d76       Write_rows: table id 12120 flags: STMT_END_F

BINLOG '
n56FZhOeuRWsnAAAAFpnAAAAAFgvAAAAAAEAB2Fyc3ZjZGIAHHRibF9maXJlZXllX3Njb3JlX3Jl
c3VsdF9sb2cAIQgP/g/+/g/+D/7+Dw8PDwMD/v4PDwMDAwMDDxEREREPDy7AAP4GwAD+GP4YwAD+
GMAA/sD+wGAAwAAMAAwA/gz+DAAGAGAwAAMDAwPAAMAA/v///wH5R8Yr
n56FZh6euRWsRgAAAKBnAAAAAFgvAAAAAAEAAgAh///+//+P/xsAAAAAAAAAZwITcAisZoWe
nyBEZoWenyBEdo1f2Q==
'/*!*/;
# at 26528
#240704  2:55:27 server id 2887104926  end_log_pos 26559 CRC32 0xc31746b9       Xid = 25815548
COMMIT/*!*/;
# at 26559
#240704  3:25:13 server id 2887104926  end_log_pos 26624 CRC32 0x407ab7a3       GTID    last_committed=44       sequence_number=45      rbr_only=yes

上面的只能看到操作记录,看不到binlog中各个字段的值,加个参数-v,可以查看到详细信息,查看各个字段的值:

sql 复制代码
mysqlbinlog  --base64-output=DECODE-PORS  
-v  
--read-from-remote-server 
--host=XXX --port=XXX 
--user=XXX --password=XXXX  
f266a8f5-binlog.000004 
sql 复制代码
# at 28164
#240704  7:12:48 server id 2887104926  end_log_pos 28195 CRC32 0xdbbed266       Xid = 25898559
COMMIT/*!*/;
# at 28195
#240704  7:13:21 server id 2887104926  end_log_pos 28260 CRC32 0xb2e7ff1f       GTID    last_committed=48       sequence_number=49      rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'c7a8d514-e0a6-11ee-9fe0-b22ac778b3fb:121'/*!*/;
# at 28260
#240704  7:13:21 server id 2887104926  end_log_pos 28347 CRC32 0x7cea87c4       Query   thread_id=5711004       exec_time=0     error_code=0
SET TIMESTAMP=1720048401.432573/*!*/;
BEGIN
/*!*/;
# at 28347
#240704  7:13:21 server id 2887104926  end_log_pos 28503 CRC32 0x6d70dd21       Table_map: `testdb`.`test_result_log` mapped to number 12120
# at 28503
#240704  7:13:21 server id 2887104926  end_log_pos 28573 CRC32 0xdb7f4839       Write_rows: table id 12120 flags: STMT_END_F
### INSERT INTO `testdb`.`test_result_log`
### SET
###   @1=32
###   @2=NULL
###   @3=NULL
###   @4=NULL
###   @5=NULL
###   @6=NULL
###   @7=NULL
###   @8=NULL
###   @9=NULL
###   @10=NULL
###   @11=NULL
###   @12=NULL
###   @13=NULL
###   @14=NULL
###   @15=NULL
###   @16=NULL
###   @17=NULL
###   @18=NULL
###   @19=NULL
###   @20=NULL
###   @21=NULL
###   @22=NULL
###   @23=NULL
###   @24=NULL
###   @25=NULL
###   @26=NULL
###   @27=NULL
###   @28=NULL
###   @29=1728616271.222
###   @30=1720048401.432
###   @31=1720048401.432
###   @32=NULL
###   @33=NULL

@中的1-33就依次是表中字段。

备注说明:mysqlbinlog 各参数含义:

sql 复制代码
基本参数
1、--start-position=POSITION
含义:从指定的 binlog 文件位置开始读取事件。
使用场景:需要从特定位置开始解析 binlog 时使用。

2、--stop-position=POSITION
含义:在指定的 binlog 文件位置停止读取事件。
使用场景:需要在特定位置停止解析 binlog 时使用。

3、--start-datetime=DATETIME
含义:从指定的日期和时间开始读取事件(格式:YYYY-MM-DD HH:MM
)。
使用场景:需要从特定时间点开始解析 binlog 时使用。
4、--stop-datetime=DATETIME
含义:在指定的日期和时间停止读取事件(格式:YYYY-MM-DD HH:MM
)。
使用场景:需要在特定时间点停止解析 binlog 时使用。


连接和输出相关参数
1、--host=HOST
含义:指定 MySQL 服务器的主机名。
使用场景:从远程服务器读取 binlog 时使用。

2、--port=PORT
含义:指定 MySQL 服务器的端口号。
使用场景:从远程服务器读取 binlog 时使用。

3、--user=USER

含义:指定用于连接 MySQL 服务器的用户名。
使用场景:从远程服务器读取 binlog 时使用。

4、--password=PASSWORD
含义:指定用于连接 MySQL 服务器的密码。
使用场景:从远程服务器读取 binlog 时使用。

5、--result-file=FILE
含义:将输出写入指定的文件。
使用场景:需要将解析结果保存到文件时使用。


过滤和格式相关参数
1、--base64-output=DECODE-ROWS
含义:将基于 Base64 编码的行事件解码为人类可读的格式。
使用场景:当 binlog 以 ROW 格式记录时,这个参数非常有用。

2、-v, --verbose
含义:详细模式,提供更多的上下文信息,包括表结构和事件的详细信息。
使用场景:需要详细了解 binlog 事件内容时使用。

3、--server-id=ID
含义:指定服务器 ID,只显示由该服务器生成的事件。
使用场景:过滤出特定服务器生成的 binlog 事件。

4、--database=DB_NAME
含义:只显示指定数据库的事件。
使用场景:需要解析特定数据库的 binlog 事件时使用。

5、--binlog-do-db=DB_NAME
含义:处理包含指定数据库更改的 binlog 事件。
使用场景:需要处理指定数据库的 binlog 时使用。

6、--binlog-ignore-db=DB_NAME
含义:忽略包含指定数据库更改的 binlog 事件。
使用场景:需要忽略指定数据库的 binlog 时使用。

7、--include-gtids=GTID_SET
含义:只包含指定 GTID 集的事件。
使用场景:需要解析特定 GTID 范围内的 binlog 事件时使用。

8、--skip-gtids
含义:不显示 GTID 事件。
使用场景:需要忽略 GTID 事件时使用。

9、--rewrite-db=OLD_NAME->NEW_NAME
含义:将事件中的数据库名称从 OLD_NAME 改写为 NEW_NAME。
使用场景:需要更改 binlog 事件中的数据库名称时使用。
4.2、定位到日志记录

从同步工具中找到报错时间的GTID值:c7a8d514-e0a6-11ee-9fe0-b22ac778b3fb:92,

①拉取mysql服务器的binlog

把binlog日志拉取到本地:

sql 复制代码
mysqlbinlog 
 --base64-output=DECODE-PORS 
 -v  
 --read-from-remote-server 
 --host=XXX --port=XXX 
 --user=XXX --password=XXXX  
 f266a8f5-binlog.000004 > test.binlog

②过滤查看binlog日志中GTID所在的日志

bash 复制代码
cat test.binlog |grep 'c7a8d514-e0a6-11ee-9fe0-b22ac778b3fb:92' -A200 -B200

日志结果如下:

sql 复制代码
SET @@SESSION.GTID_NEXT= 'c7a8d514-e0a6-11ee-9fe0-b22ac778b3fb:92'/*!*/;
# at 16340
#240703  7:17:39 server id 2887104926  end_log_pos 16415 CRC32 0xa9fa9a00       Query   thread_id=5609352       exec_time=0     error_code=0
SET TIMESTAMP=1719962259/*!*/;
BEGIN
/*!*/;
# at 16415
#240703  7:17:39 server id 2887104926  end_log_pos 16571 CRC32 0x96aae420       Table_map: `testdb`.`test_result_log` mapped to number 12120
# at 16571
#240703  7:17:39 server id 2887104926  end_log_pos 16712 CRC32 0x5debd65f       Delete_rows: table id 12120 flags: STMT_END_F
### DELETE FROM `testdb`.`test_result_log`
### WHERE
###   @1=1
###   @2='123456789'
###   @3=NULL
###   @4=NULL
###   @5=NULL
###   @6=NULL
###   @7=NULL
###   @8=NULL
###   @9=NULL
###   @10=NULL
###   @11=NULL
###   @12=NULL
###   @13=NULL
###   @14=NULL
###   @15=NULL
###   @16=NULL
###   @17=NULL
###   @18=NULL
###   @19=NULL
###   @20=NULL
###   @21=NULL
###   @22=NULL
###   @23=NULL
###   @24=NULL
###   @25=NULL
###   @26=NULL
###   @27=NULL
###   @28=NULL
###   @29=NULL
###   @30=1719962114.600
###   @31=1719962114.600
###   @32=NULL
###   @33=NULL
### DELETE FROM `testdb`.`tbl_test_result_log`
### WHERE
###   @1=2
4.3、问题定位

发现有删除记录的操作,这条记录正好是有问题的记录 @29=NULL,但是这个字段(rec_dt)是我用于分表的字段,lua中值为nil 导致同步异常。

问题终于定位到了,是因为有人测试时插入数据插入错了,第一次插入式忘记给这个字段赋值了,然后发现不对当时删除了。(@29 这个字段rec_dt没有设置默认值,后面就为NULL了)

4.4、为什么重启可以同步?

那还有诡异的,为什么重启后数据可以同步呢?按道理不应该卡着,都同步不了吗?

最后发现同步工具在目标端配置了多线程执行(thread_num: 64 #后端执行线程数量),同步的时候并不是串行的,即:源端和目标端的数据入库顺序可能不是完全一样的。

5、总结

①lua脚本要写的健壮,异常要考虑到位。

②有问题就去看看binlog,定位一下具体是什么原因。

③分表字段建议设置默认值,不然没办法分片。

大家使用过哪些数据库同步工具?是并行还是串行的?和数据库本身的主备同步有差别吗?

相关推荐
TDengine (老段)27 分钟前
TDengine 时间函数 TODAY() 用户手册
大数据·数据库·物联网·oracle·时序数据库·tdengine·涛思数据
码界奇点36 分钟前
KingbaseES一体化架构与多层防护体系如何保障企业级数据库的持续稳定与弹性扩展
数据库·架构·可用性测试
悟乙己1 小时前
数据科学家如何更好地展示自己的能力
大数据·数据库·数据科学家
皆过客,揽星河1 小时前
mysql进阶语法(视图)
数据库·sql·mysql·mysql基础语法·mysql进阶语法·视图创建修改删除
tuokuac2 小时前
Redis 的相关文件作用
数据库·redis·缓存
鹧鸪云光伏与储能软件开发3 小时前
投资储能项目能赚多少钱?小程序帮你测算
运维·数据库·小程序·光伏·光伏设计软件·光伏设计
2301_779503765 小时前
MySQL主从同步--主从复制进阶
数据库·mysql
beijingliushao5 小时前
58-正则表达式
数据库·python·mysql·正则表达式
诗句藏于尽头5 小时前
DJANGO后端服务启动报错及解决
数据库·笔记·django
手握风云-5 小时前
MySQL数据库精研之旅第十五期:索引的 “潜规则”(下)
数据库