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,定位一下具体是什么原因。
③分表字段建议设置默认值,不然没办法分片。
大家使用过哪些数据库同步工具?是并行还是串行的?和数据库本身的主备同步有差别吗?