MySQL从库的Crash Safe
2017-10-17
最近测试环境的一个三节点的MySQL GR集群(单主)的一个从节点发生了宕机,重启恢复后,过了一会监控系统告警,刚刚宕机的这个节点退出了GR集群。原因是主库上对某条数据的修改复制到从库后,在这个从库上找不到这条数据(slave_exec_mode=STRICT
),第一反应就是主库和从库出现了不一致。因为是测试环境,数据量不大,所以采取的方案是将从库重建(团队没有专门的DBA)。接下来特意查看了一下MySQL复制和从库宕机恢复这块的资料。
Crah Safe是MySQL 5.6新增的特性,主要是为了解决之前版本中因系统异常宕机而引起的SQL线程信息错误的问题。我们知道在作为从库角色的MySQL使用两个线程:
- IO线程:负责把主库的二进制日志(bin log)复制到本地中继日志(relay log)
- SQL线程:负责执行中继日志(relay log)中的SQL
这两个线程都需要记录当前日志的位置,之前的版本是记录到文件中,IO线程记录到master.info文件,SQL线程记录到relay-log.info文件。当从节点重启就可以根据relay-log.info中记录的位置继续同步。因为文件系统会自己控制缓存刷新,所以master.info文件和relay-log.info文件的写入位置在发生宕机的情况下可能是不准确的。MySQL提供了sync_master_info
和sync_relay_log_info
来控制在完成配置的事务数后同步到磁盘(默认值在MySQL 5.7中都是10000),可以设置sync_master_info=1
和sync_relay_log_info=1
即每个事务完成后都同步磁盘,但是这样对磁盘的消耗很大,即使都设置成1,实际还是会出现数据不一致,因为涉及到文件操作和数据库操作,有时无法实现原子性。
MySQL 5.6提供了Crash Safe Slave的特性,通过设置master_info_repository=TABLE
和relay_log_info_repository=TABLE
将复制信息保存到表中,mysql.slave_master_info
表和mysql.slave_relay_log_info
表,这样就都是数据库操作了,可以将复制信息的更新放到事务中,确保数据一致。
再来看一下IO线程和SQL线程:
- IO线程会在
sync_master_info
个事务后更新一次mysql.slave_master_info
表,前面给出了sync_master_info
的默认值是10000,也就是每提交10000个事务更新一次,这是不符合Crash Safe的,可以设置sync_master_info=1
,但这会影响性能。 - SQL线程会在一个事务中完成数据同步和对
mysql.slave_relay_log_info
表的更新
学习到这里,就找到了我们这次从库宕机出现数据不一致的原因了,我们配置了master_info_repository=TABLE
和relay_log_info_repository=TABLE
,而没有配置sync_master_info
,所以默认值是10000,因此这个从库不是Crash Safe的。处于性能的考虑,不能设置sync_master_info=1
,实际上MySQL提供了一种更好的方式,即配置 relay_log_recovery = ON
,当MySQL重启时,会从mysql.slave_relay_log_info
恢复IO线程的最新位置,根据这个位置重新从主库获取bin log,这样就避免了由于未刷盘导致的relay log文件不完整造成的数据不一致。
当前相关的配置如下:
1gtid_mode=ON
2enforce_gtid_consistency=ON
3master_info_repository=TABLE
4relay_log_info_repository=TABLE
5relay_log_recovery = ON
6binlog_checksum=NONE
7log_slave_updates=ON