如何通过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,定位一下具体是什么原因。

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

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

相关推荐
daxian_am46132 分钟前
k8s image error
java·数据库·kubernetes
开心工作室_kaic1 小时前
基于微信小程序的校园失物招领系统的设计与实现(论文+源码)_kaic
c语言·javascript·数据库·vue.js·c#·旅游·actionscript
bin91531 小时前
【EXCEL数据处理】000009 案列 EXCEL单元格数字格式。文本型数字格式和常规型数字格式的区别
大数据·前端·数据库·信息可视化·数据分析·excel·数据可视化
小桑要读研1 小时前
Redis实现点赞
java·mysql·mybatis
bug菌¹2 小时前
滚雪球学Oracle[2.1讲]:Oracle数据库安装与配置
数据库·oracle
Data 3172 小时前
Hive数仓操作(十四)
大数据·数据库·数据仓库·hive·hadoop
丶只有影子2 小时前
SkyWalking监控SQL参数
数据库·sql·skywalking
wdxylb2 小时前
MySQL数据库用户权限控制的实现方法
数据库·mysql·oracle
bug菌¹2 小时前
滚雪球学Oracle[6.1讲]:高级特性与实战案例
数据库·oracle
Data 3172 小时前
Hive数仓操作(十五)
大数据·数据库·数据仓库·hive·hadoop