
作者 罗小波·沃趣科技高级数据库技术专家
出品 沃趣科技
某客户反馈,在利用binlog做基于时间点的恢复时,频繁出现无法正确恢复数据的情况(出现概率大约5%~10%)。 数据库环境为MySQL 5.7.27版本的级联复制拓扑(A->B->C),主库A提供写服务,从库B提供读服务,级联复制从库C提供数据的备份和恢复服务。其中,AB库为生产服务库,数据的备份和恢复服务是利用级联复制从库C的数据做定期与手动快照,然后结合从库C的binlog做基于时间点的数据恢复。无法正确恢复数据的情况,具体指的就是实际恢复出来的数据对应的时间点比mysqlbinlog命令指定的时间点要早很多(例如:主库数据已经到达12:00、从库C的快照时间点是10:00、执行恢复操作命令指定的截止时间点是11:00、最终恢复出来的数据时间点是10:30;或者干脆最终恢复出来的数据只有快照数据——即只有10:00的数据,没有任何binlog补偿的数据)。
咋一看,有点懵,脑子里迅速推导了几种可能性(不是很靠谱那种)。
* 执行备份恢复这一套系统逻辑有BUG导致的问题。
* mysqlbinlog解析工具有BUG、或者MySQL Server在从库端写binlog有BUG导致的问题、或者高负载导致MySQL的脑子坏了?
* 一些未暴露出来的骚操作导致的问题。
显然,通过这些简单的信息和粗略的推导分析,并不能确定问题的真相,而要确定问题的真相,就需要更详尽的信息以及可复现的方法。下面,我们将遵循这个套路带着大家一步一步剖析问题的真相。
1、信息收集
收集跟问题现象直接相关的数据,用于直观感受问题具体长什么样子。 * 跟问题现象相关的操作有哪些,具体是如何操作的。
* 在从库C中解析binlog的原始命令文本,以及对应的binlog原始文件。
* 从库C复制状态与延迟信息。
* 从库C的my.cnf配置模板。
* 从库C中的事务信息、锁信息、show processlist线程状态信息。收集从库C的主机负载信息和数据库的负载信息,用于判断一些可能由于高负载导致的问题。 收集从库C的MySQL错误日志、操作系统常规日志、操作系统安全日志,用于判断是否有某些错误或者BUG被触发。
2、分析排查
并没有发现什么明显的骚操作,排除人为操作事务的可能性。 从库延迟平均在5~20小时(需要引起注意)。 通过mysqlbinlog解析并查看从库C原始的binlog文件后发现,大多数binlog中记录的内容没有发现异常,但有极少数的binlog中的event时间戳顺序是乱序的(需要引起注意): mysqlbinlog解析命令指定的--stop-datetime时间戳为'19:08:10'(需要引起注意)。 配置模板没有发现什么异常,排除参数配置不当导致的可能性。 数据库线程状态无异常、无大事务、也没有长时间的锁等待信息,排除大事务、锁等待、长时间未提交的事务导致的可能性。
* 记录数据的event(GTID、Query、Table_map、Update_rows|Delete_rows|Update_rows、Xid)时间戳范围为 19:03:30 ~ 19:12:34
* Rotate类型的event时间戳为19:12:34
3、找到原因
# 停止SQL线程
root@localhost : (none) 05:21:12> stop slave sql_thread;
Query OK, 0 rows affected (0.00 sec)
# 为了便于后续查看数据以及避免之前记录的binlog内容的干扰,先手动在从库中切换binlog
root@localhost : (none) 05:21:31> flush binary logs;
Query OK, 0 rows affected (0.01 sec)
root@localhost : (none) 05:22:00> show binary logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 60392080 |
......
| mysql-bin.000010 | 194 | # 滚动之后,罪行的binlog file为mysql-bin.000010
+------------------+-----------+
10 rows in set (0.00 sec)复制
# 启动SQL线程
root@localhost : (none) 05:22:13> start slave sql_thread;
Query OK, 0 rows affected (0.00 sec)
# 查看复制状态
root@localhost : (none) 05:30:08> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.10.30.163
Master_User: repl
Master_Port: 3306
Connect_Retry: 10
Master_Log_File: mysql-bin.000015
Read_Master_Log_Pos: 375161305
Relay_Log_File: mysql-relay-bin.000026
Relay_Log_Pos: 148909788
Relay_Master_Log_File: mysql-bin.000009
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
......
Exec_Master_Log_Pos: 148909575
Relay_Log_Space: 3596749378
......
Seconds_Behind_Master: 648 # 成功制造复制延迟为648秒(当然,这个延迟时间可能不精确,但这里不是重点)
......复制
在从库中使用mysqlbinlog命令解析查看mysql-bin.000010文件中的内容。
[root@node3 binlog]# ll
total 4836776
......
-rw-r----- 1 mysql mysql 536903179 Jul 29 17:31 mysql-bin.000010
-rw-r----- 1 mysql mysql 21004292 Jul 29 17:31 mysql-bin.000011
-rw-r----- 1 mysql mysql 572 Jul 29 17:31 mysql-bin.index
# 解析文件mysql-bin.000010
[root@node3 binlog]# mysqlbinlog mysql-bin.000010 > a.sql
# 过滤一下a.sql中带server id的数据行,也就是标记event类型的那些文本行
[root@node3 binlog]# grep -E 'server id|GTID_NEXT' a.sql > a.txt
# 查看两个文件的大小
[root@node3 binlog]# ls -lh a.*
-rw-r--r-- 1 root root 878M Jul 29 17:32 a.sql
-rw-r--r-- 1 root root 322M Jul 29 17:44 a.txt复制
在从库中使用vim打开a.txt文件,查看所有的event的时间戳信息。
[root@node3 binlog]# vim a.txt
# 发现binlog中event的时间戳乱序了,内容如下(FDE、Previous-GTIDs的时间戳为190729 17:22:00,记录数据的GTID、Query、Table_map、Update_rows|Delete_rows|Update_rows、Xid时间戳在190729 17:19:18 ~ 190729 17:20:57之间,Rotate时间戳为190729 17:20:57)
#190729 17:22:00 server id 33062 end_log_pos 123 CRC32 0xc3a9ea93 Start: binlog v 4, server v 5.7.27-log created 190729 17:22:00
#190729 17:22:00 server id 33062 end_log_pos 194 CRC32 0x22797305 Previous-GTIDs
#190729 17:19:18 server id 33061 end_log_pos 259 CRC32 0xd48537a6 GTID last_committed=0 sequence_number=1 rbr_only=yes
SET @@SESSION.GTID_NEXT= 'd9c5c228-b1e0-11e9-b14d-525400c33752:258161'/*!*/;
#190729 17:19:18 server id 33061 end_log_pos 327 CRC32 0x78818eeb Query thread_id=364 exec_time=650 error_code=0
#190729 17:19:18 server id 33061 end_log_pos 451 CRC32 0x6ca05182 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:19:18 server id 33061 end_log_pos 867 CRC32 0xce1dc10f Update_rows: table id 115 flags: STMT_END_F
#190729 17:19:18 server id 33061 end_log_pos 986 CRC32 0xf4d017ed Table_map: `sbtest`.`sbtest2` mapped to number 114
#190729 17:19:18 server id 33061 end_log_pos 1211 CRC32 0xbbaebd66 Delete_rows: table id 114 flags: STMT_END_F
#190729 17:19:18 server id 33061 end_log_pos 1541 CRC32 0x14f2e853 Table_map: `sbtest`.`sbtest2` mapped to number 114
#190729 17:19:18 server id 33061 end_log_pos 1766 CRC32 0xd9080627 Write_rows: table id 114 flags: STMT_END_F
#190729 17:19:18 server id 33061 end_log_pos 1797 CRC32 0x1d0265e4 Xid = 1084024
......
#190729 17:20:57 server id 33061 end_log_pos 536900936 CRC32 0x12796d01 GTID last_committed=260141 sequence_number=260157 rbr_only=yes
SET @@SESSION.GTID_NEXT= 'd9c5c228-b1e0-11e9-b14d-525400c33752:518317'/*!*/;
#190729 17:20:57 server id 33061 end_log_pos 536901004 CRC32 0x8eb6df8a Query thread_id=360 exec_time=622 error_code=0
#190729 17:20:57 server id 33061 end_log_pos 536901128 CRC32 0x3e09a82e Table_map: `sbtest`.`sbtest2` mapped to number 114
#190729 17:20:57 server id 33061 end_log_pos 536901544 CRC32 0xa97d84de Update_rows: table id 114 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 536901786 CRC32 0x35aec8ff Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 536902202 CRC32 0x05064820 Update_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 536902321 CRC32 0x293c82e4 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 536902546 CRC32 0xe4bfcf8b Delete_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 536902876 CRC32 0x1fbc35a7 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 536903101 CRC32 0x45c6cc8e Write_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 536903132 CRC32 0xc6b80fb0 Xid = 2284911
#190729 17:20:57 server id 33062 end_log_pos 536903179 CRC32 0x23a1bcf6 Rotate to mysql-bin.000011 pos: 4复制
开心 + 激动,赶紧在从库中用mysqlbinlog命令解析一下,这里只需要指定--stop-datetime时间戳小于190729 17:22:00,但大于190729 17:19:18,然后解析数据为空,就确认问题复现了。
# 这里我们就指定--stop-datetime时间戳为190729 17:20:00
[root@node3 binlog]# mysqlbinlog --stop-datetime='2019-07-29 17:20:00' mysql-bin.000010 > b.sql
[root@node3 binlog]# ls -lh b.sql
-rw-r--r-- 1 root root 336 Jul 29 17:37 b.sql复制
在从库中,使用vim打开b.sql,嘿,果然发现没有数据,问题现象确认复现了。
# 一字不漏的码出来,就这么点了,没有任何数据相关的event ...
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;复制
so...到这里,我们成功复现了问题现象,但对于前面提出的三个问题,现在,好像一个都没解决呢,现在回顾一下这三个问题:
第一个问题:复制延迟与基于时间点恢复数据不正确有啥关系? 第二个问题:binlog中的event时间戳顺序乱序是怎么回事? 第三个问题:mysqlbinlog解析命令指定时间戳为'2019-07-29 17:20:00',是否是碰到了FDE、Previous-GTIDs类型的event时间戳为190729 17:22:00时,发现不满足时间范围条件,直接跳过了解析后续的event?"
# 尴尬的是,不知道这两个GTID在主库的哪个binlog中,只能从第一个binlog开始,挨个向后解析主库binlog,并查看前50行中的Previous-GTIDs event中的GTID SET来判断,通过逐个解析binlog查看,发现我们需要寻找的两个GTID藏在主库的mysql-bin.000009 和mysql-bin.000010两个二进制日志文件中
# 解析mysql-bin.000009 文件
[root@node2 binlog]# mysqlbinlog mysql-bin.000009 |head -50
......
'/*!*/;
# at 123
#190729 17:19:01 server id 33061 end_log_pos 194 CRC32 0xf53f1220 Previous-GTIDs
# d9c5c228-b1e0-11e9-b14d-525400c33752:1-214532
......
# 解析mysql-bin.000010文件
[root@node2 binlog]# mysqlbinlog mysql-bin.000010 |head -50
......
'/*!*/;
# at 123
#190729 17:20:41 server id 33061 end_log_pos 194 CRC32 0x27fe0eac Previous-GTIDs
# d9c5c228-b1e0-11e9-b14d-525400c33752:1-474622
# 解析mysql-bin.000011文件
[root@node2 binlog]# mysqlbinlog mysql-bin.000011 |head -50
......
'/*!*/;
# at 123
#190729 17:22:17 server id 33061 end_log_pos 194 CRC32 0x1457abd9 Previous-GTIDs
# d9c5c228-b1e0-11e9-b14d-525400c33752:1-731105复制
# 解析binlog
[root@node2 binlog]# mysqlbinlog mysql-bin.000009 mysql-bin.000010 --include-gtids='d9c5c228-b1e0-11e9-b14d-525400c33752:258161:518317' > c.sql
# 过滤出event事件类型的信息行
[root@node2 binlog]# grep -E 'server id|GTID_NEXT' c.sql > c.txt
# 查看一下c.sql和c.txt两个文件的大小
[root@node2 binlog]# ls -lh c.*
-rw-r--r-- 1 root root 102M Jul 29 18:16 c.sql
-rw-r--r-- 1 root root 3.3K Jul 29 18:18 c.txt
# vim查看c.txt文件中的内容
[root@node2 binlog]# vim c.txt
#190729 17:19:01 server id 33061 end_log_pos 123 CRC32 0xfade248c Start: binlog v 4, server v 5.7.27-log created 190729 17:19:01
#190729 17:19:01 server id 33061 end_log_pos 194 CRC32 0xf53f1220 Previous-GTIDs
# GTID为 d9c5c228-b1e0-11e9-b14d-525400c33752:258161 的binlog event时间戳与从库中的binlog event时间戳完全相同
#190729 17:19:18 server id 33061 end_log_pos 88394786 CRC32 0x7a13b001 GTID last_committed=43590 sequence_number=43629 rbr_only=yes
SET @@SESSION.GTID_NEXT= 'd9c5c228-b1e0-11e9-b14d-525400c33752:258161'/*!*/;
#190729 17:19:18 server id 33061 end_log_pos 88394865 CRC32 0x1cb7f558 Query thread_id=364 exec_time=0 error_code=0
#190729 17:19:18 server id 33061 end_log_pos 88394989 CRC32 0xf07fac6f Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:19:18 server id 33061 end_log_pos 88395405 CRC32 0x8a0adc66 Update_rows: table id 115 flags: STMT_END_F
#190729 17:19:18 server id 33061 end_log_pos 88395524 CRC32 0x60282fdb Table_map: `sbtest`.`sbtest2` mapped to number 114
#190729 17:19:18 server id 33061 end_log_pos 88395749 CRC32 0xe041108a Delete_rows: table id 114 flags: STMT_END_F
#190729 17:19:18 server id 33061 end_log_pos 88396079 CRC32 0x1d8f8fd2 Table_map: `sbtest`.`sbtest2` mapped to number 114
#190729 17:19:18 server id 33061 end_log_pos 88396304 CRC32 0x704462b2 Write_rows: table id 114 flags: STMT_END_F
#190729 17:19:18 server id 33061 end_log_pos 88396335 CRC32 0xbf367aab Xid = 5087302
#190729 17:20:41 server id 33061 end_log_pos 536892835 CRC32 0x864ece23 Rotate to mysql-bin.000010 pos: 4
......
#190729 17:20:41 server id 33061 end_log_pos 123 CRC32 0x489dccb5 Start: binlog v 4, server v 5.7.27-log created 190729 17:20:41
#190729 17:20:41 server id 33061 end_log_pos 194 CRC32 0x27fe0eac Previous-GTIDs
# GTID为 d9c5c228-b1e0-11e9-b14d-525400c33752:518317 的binlog event时间戳仍然与从库中的binlog event时间戳完全相同
#190729 17:20:57 server id 33061 end_log_pos 91264585 CRC32 0xc7ea8f93 GTID last_committed=43656 sequence_number=43695 rbr_only=yes
SET @@SESSION.GTID_NEXT= 'd9c5c228-b1e0-11e9-b14d-525400c33752:518317'/*!*/;
#190729 17:20:57 server id 33061 end_log_pos 91264664 CRC32 0x7b4539d5 Query thread_id=360 exec_time=0 error_code=0
#190729 17:20:57 server id 33061 end_log_pos 91264788 CRC32 0xf11c5c66 Table_map: `sbtest`.`sbtest2` mapped to number 114
#190729 17:20:57 server id 33061 end_log_pos 91265204 CRC32 0x856ae255 Update_rows: table id 114 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 91265446 CRC32 0xdcd7ee6b Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 91265862 CRC32 0x3a8a4aa2 Update_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 91265981 CRC32 0xf51f1fc2 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 91266206 CRC32 0xc52d3e94 Delete_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 91266536 CRC32 0xff5e2784 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 91266761 CRC32 0x708a7aaa Write_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 91266792 CRC32 0xcde1e798 Xid = 10289372
......复制
# 解析mysql-bin.000011
[root@node3 binlog]# mysqlbinlog mysql-bin.000011 |grep -E 'server id|GTID_NEXT' > d.txt
# vim查看d.txt文件内容,并没有发现FDE、Previous-GTIDs时间戳比事务的binlog event时间戳大的情况(WTF...,这是神马情况?)
[root@node3 binlog]# vim d.txt
#190729 17:20:57 server id 33062 end_log_pos 123 CRC32 0xa03c5bbc Start: binlog v 4, server v 5.7.27-log created 190729 17:20:57
#190729 17:20:57 server id 33062 end_log_pos 194 CRC32 0xfff79f0a Previous-GTIDs
#190729 17:20:57 server id 33061 end_log_pos 259 CRC32 0x37ef05b1 GTID last_committed=0 sequence_number=1 rbr_only=yes
SET @@SESSION.GTID_NEXT= 'd9c5c228-b1e0-11e9-b14d-525400c33752:518318'/*!*/;
#190729 17:20:57 server id 33061 end_log_pos 327 CRC32 0xe88d5be8 Query thread_id=332 exec_time=622 error_code=0
#190729 17:20:57 server id 33061 end_log_pos 451 CRC32 0x3f4748f8 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 867 CRC32 0x6cab4c30 Update_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 986 CRC32 0x8f8db6cc Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 1211 CRC32 0x49418317 Delete_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 1541 CRC32 0x6faf4972 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:20:57 server id 33061 end_log_pos 1766 CRC32 0x35b274e6 Write_rows: table id 115 flags: STMT_END_F
#190729 17:20:57 server id 33061 end_log_pos 1797 CRC32 0x70757956 Xid = 2284921
......
#190729 17:22:34 server id 33062 end_log_pos 536885932 CRC32 0xe8e5a7b3 Rotate to mysql-bin.000012 pos: 4
# 继续解析mysql-bin.000012
[root@node3 binlog]# mysqlbinlog mysql-bin.000012 |grep -E 'server id|GTID_NEXT' > e.txt
# vim查看e.txt文件内容,仍然没有发现FDE、Previous-GTIDs时间戳比事务的binlog event时间戳大的情况
[root@node3 binlog]# vim e.txt
#190729 17:22:34 server id 33062 end_log_pos 123 CRC32 0x55c719a6 Start: binlog v 4, server v 5.7.27-log created 190729 17:22:34
#190729 17:22:34 server id 33062 end_log_pos 194 CRC32 0x246c1b20 Previous-GTIDs
#190729 17:22:34 server id 33061 end_log_pos 259 CRC32 0x5dbbac77 GTID last_committed=0 sequence_number=1 rbr_only=yes
SET @@SESSION.GTID_NEXT= 'd9c5c228-b1e0-11e9-b14d-525400c33752:776005'/*!*/;
#190729 17:22:34 server id 33061 end_log_pos 327 CRC32 0x86ca88e5 Query thread_id=331 exec_time=593 error_code=0
#190729 17:22:34 server id 33061 end_log_pos 451 CRC32 0xc2c13e95 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:22:34 server id 33061 end_log_pos 867 CRC32 0xd95ec374 Update_rows: table id 115 flags: STMT_END_F
#190729 17:22:34 server id 33061 end_log_pos 1109 CRC32 0xc698d4ca Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:22:34 server id 33061 end_log_pos 1525 CRC32 0x893ec0b8 Update_rows: table id 115 flags: STMT_END_F
#190729 17:22:34 server id 33061 end_log_pos 1644 CRC32 0x503ef822 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:22:34 server id 33061 end_log_pos 1869 CRC32 0x56078d13 Delete_rows: table id 115 flags: STMT_END_F
#190729 17:22:34 server id 33061 end_log_pos 2199 CRC32 0xb134e795 Table_map: `sbtest`.`sbtest1` mapped to number 115
#190729 17:22:34 server id 33061 end_log_pos 2424 CRC32 0x0bf4a350 Write_rows: table id 115 flags: STMT_END_F
#190729 17:22:34 server id 33061 end_log_pos 2455 CRC32 0x4d065250 Xid = 3484259
......
#190729 17:24:12 server id 33062 end_log_pos 536888910 CRC32 0x029b5df4 Rotate to mysql-bin.000013 pos: 4复制
# max_binlog_size系统变量的设置
root@localhost : (none) 06:56:20> show variables like '%max_binlog_size%';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| max_binlog_size | 536870912 | # 转换为M单位就是512M
+-----------------+-----------+
1 row in set (0.00 sec)
# 查看binlog文件的大小
[root@node3 binlog]# ls -lh
total 9.4G
......
-rw-r----- 1 mysql mysql 176615208 Jul 29 17:22 mysql-bin.000009 # 在mysql-bin.000009的时候执行了flush binary logs切换到了mysql-bin.000010 ,所以mysql-bin.000009的文件大小小很多
-rw-r----- 1 mysql mysql 536903179 Jul 29 17:31 mysql-bin.000010 # 转换为M单位就是512M,刚好达到max_binlog_size系统变量的设置
-rw-r----- 1 mysql mysql 536885932 Jul 29 17:32 mysql-bin.000011
-rw-r----- 1 mysql mysql 536888910 Jul 29 17:33 mysql-bin.000012
-rw-r----- 1 mysql mysql 536887458 Jul 29 17:34 mysql-bin.000013
-rw-r----- 1 mysql mysql 536888575 Jul 29 17:35 mysql-bin.000014
-rw-r----- 1 mysql mysql 536903940 Jul 29 17:37 mysql-bin.000015
-rw-r----- 1 mysql mysql 491243565 Jul 29 18:14 mysql-bin.000016
-rw-r----- 1 mysql mysql 832 Jul 29 17:37 mysql-bin.index复制
至此,我们离真相更进一步了,知道了从库中binlog文件 mysql-bin.000010 中的时间戳乱序是因为手动执行了flush binary logs语句切换日志导致的,而其他的binlog是自动触发滚动生成的就没有这个问题。但是。。还是比较迷糊,这两种方式产生的binlog中的FDE、Previous-GTIDs时间戳一定有蹊跷,如果要直接去找出在两种情况下各自获取时间戳的时机可能就比较复杂,这里我们采用了反证法,在复制存在延迟的情况下,先在从库中把复制线程停掉并执行flush binary logs; 发现FDE、Previous-GTIDs时间戳是从库实例的当前时间戳,然后,在从库中重新启动复制线程,让从库的binlog文件大小达到max_binlog_size系统变量的设置时自动切换,发现FDE、Previous-GTIDs时间戳是来自上一个binlog文件中最后一个事务对应的event的时间戳(即,自动切换的binlog中的FDE、Previous-GTIDs时间戳是跟随主库binlog event的,整个binlog中的event时间戳不会出现乱序,在使用mysqlbinlog解析binlog时就不可能出现无法解析事务对应的event的问题),有兴趣的同学可自行验证,这里不再赘述。
源码文件mysqlbinlog.cc中处理时间戳的逻辑代码如下。
调试测试数据的binlog中的event数据如下。
执行mysqlbinlog解析的命令如下。
gdb调试信息如下,可以直观地看到,在处理FORMAT_DESCRIPTION_EVENT(FDE)的event时,直接goto end退出了。
现在,第三个问题也解决了,还剩下第一个问题:复制延迟与基于时间点恢复数据不正确有啥关系?我们解决了第二个问题和第三个问题,第一个问题似乎答案也呼之欲出,我们来复盘整理一下上文中的信息。
我们在sysbench压测过程中,先停止了SQL线程,然后手工执行了flush binary logs语句切换binlog,此时切换后的binlog文件名为mysql-bin.000010,该binlog中的FDE、Previous-GTIDs时间戳是从库的当前时间戳(190729 17:22:00),此时,SQL线程未启动,也就是说不会应用relay log中来自主库的binlog,也就是说从库自身的binlog中不会写入任何数据event。
然后,过了10分钟左右,我们启动了SQL线程,开始应用来自主库的binlog,写入mysql-bin.000010文件中第一个事务相关的event时间戳为190729 17:19:18(它是来自主库写入binlog时的时间戳,不会因为从库做任何操作而改变),也就是说,此时mysql-bin.000010中的event时间戳就处于一个乱序的状态了。
然后,使用mysqlbinlog解析binlog时,指定了--stop-datetime时间戳为2019-07-29 17:20:00,该时间小于FDE、Previous-GTIDs时间戳190729 17:22:00,根据上文中gdb调试的逻辑判断,指定的时间戳小于了FDE的时间戳,不满足条件,不再往后扫描(即使后续的数据event的时间戳为190729 17:19:18,满足小于mysqlbinlog指定的时间戳也无济于事),mysqlbinlog命令退出解析任务。
导致基于时间点恢复数据不正确的原因是:在从库存在复制延迟的情况下,手工执行了flush binary logs语句切换binlog(这里要注意:从库存在复制延迟的情况下,如果重启从库数据库进程,binlog中的时间戳仍然会乱序),导致了binlog文件中的时间戳乱序,然后,mysqlbinlog工具指定了一个尴尬的时间戳(卡在了小于FDE、Previous-GTIDs的时间戳,但大于数据event的时间戳的范围内),导致解析任务退出,无法正确解析数据的event。
手工执行flush bianry logs语句。
重启从库数据库进程。
4、问题追踪
作者简介
罗小波·沃趣科技高级数据库技术专家
IT从业多年,主要负责MySQL 产品的数据库支撑与售后二线支撑。曾参与版本发布系统、轻量级监控系统、运维管理平台、数据库管理平台的设计与编写,熟悉MySQL体系结构,Innodb存储引擎,喜好专研开源技术,多次在公开场合做过线下线上数据库专题分享,发表过多篇数据库相关的研究文章。
相关链接
Oracle RAC Cache Fusion 系列九:Oracle RAC 分布式资源管理(二)
Oracle RAC Cache Fusion 系列八:Oracle RAC 分布式资源管理(一)
Oracle RAC Cache Fusion 系列七:RAC Messaging System
深入浅出Kubernetes网络:跨节点网络通信之Flannel

更多干货,欢迎来撩~