MySQL#复制 - crash-safe Replication - 上

Posted yangyidba

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL#复制 - crash-safe Replication - 上相关的知识,希望对你有一定的参考价值。

本篇文章要讨论的是复制环境下的crash-safe,换句话说的意思就是:保证无论在master还是slave发生异常crash拉起后,整个复制结构是支持ACID特性的,也意味着仅考虑支持事务的存储引擎(MyISAM场景不考虑)。本篇为上篇,内容为5.5/5.6,不包括MTS场景。5.7+ MTS场景见下篇。


主库配置

在讨论从库之前,首先,主库的不合理设置同样会在一些情况下造成从库发生复制故障,所以主库得需要是双1的。
非双1有很可能造成从库比主库多事务,所以从库异常是比较容易发生的事情。


复制中一致性保证影响因素

一句话简化看这个问题,就是保证复制过程中的两个线程:IO Thread、SQL Thread不掉链子。

IO Thread,用于接收主库binlog events,写入relay log。 
SQL Thread,用于应用relay log到数据文件中。

对于从库节点,如果发生db crash或者宕机,IO Thread要知道从哪里继续拉取binlog,同时SQL Thread要知道从relay log中的哪个位置点开始继续应用日志。即恢复之前的复制进度,不能漏应用事务,也不能重复应用事务。(类似断点续传)


所以需要有一个地方能够存储更新进度:————


mysql 5.5

5.5是远古版本,当时:

IO Thread和SQL Thread分别往master.info、relay-log.info两个文件中更新元数据。应的元数信息被称作master info”与relay log info”。

但默认情况下,这俩文件更新频次是非常低的。 

关于写master info和relay log info,有两个相关参数可以“尽量”保证。

sync_master_info,指每经过sync_master_info个events,slave将master.info文件落盘。为0时则仅靠OS来控制。

sync_relay_log_info,指每经过sync_relay_log_info个events,slave将relay-log.info文件落盘。为0时则仅靠OS来控制。

默认值为10000,很夸张。
如果将两个info参数设置为1,可保证每次接收或应用events之后,再将文件写入磁盘。

听起来很靠谱,但是这样搞会有两个问题: 
1、性能差。 
2、提交事务在先,更新复制信息到文件在后,极端情况下仍然可能出错。

上述更新过程如下: 
1、apply relay log中的事务  
2、再更新relay-log.info文件

复制相关的元数据信息出现问题会产生什么后果?举一个简单的故障例子: 
如果异常crash后拉起,SQL Thread若读到了旧的位置(relay-log.info没及时更新),这样会重新apply某个提交过的事务,“恶名昭著”的1032、1062就来了。如果没有主键或唯一键的事务可能会更糟——没办法简单通过SHOW SLAVE STATUS去检查主从数据是否不一致了。这个场景好理解。


至于写relay log这个操作本身,也是难以保证极端情况下crash是没问题的,即便是将sync_relay_log设置为1也如此。


总结如下,因为

  • master.info的位置  

  • relay-log.info的位置  

  • relay log里的内容  

三者均不100%可信(即便相关参数都设置成1)  
所以,MySQL 5.5在复制环境中是不安全的。


MySQL 5.6

有个重大改进,将master info和relay log info写入了表里,也就是对应mysql.slave_master_info和mysql.slave_relay_log_info这俩张表,该表早期还是MyISAM,需要在5.6安装好后手动改为InnoDB。当然在某个5.6的小版本之后就默认为InnoDB了。

该特性对应了两个参数:

master_info_repository = TABLE | FILE
relay_log_info_repository = TABLE | FILE 

而InnoDB就很舒服了,意味着IO Thread和SQL Thread更新对应的元数据信息时,是支持事务的,妙啊。

支持事务更新relay log info的过程就是:

START TRANSACTION;
apply log event;
UPDATE mysql.slave_relay_log_info
SET Master_log_pos = Exec_Master_Log_Pos,
Master_log_name = Relay_Master_Log_File,
Relay_log_name = Relay_Log_File,
Relay_log_pos = Relay_Log_Pos;
COMMIT;

简单想一下就可以发现,其实只需要保证relay log info不出错就行了,故只需要配置如下即可保证crash safe:

relay_log_info_repository = TABLE
relay_log_recovery = ON

补充,打开relay_log_recovery时,在重启后会发生什么呢?

  • IO Thread的位点会重新设置为SQL Thread的位点(以relay log info为准)

  • 以SQL Thread的位置创建一个新的relay log

通过上述两个参数的设置,则可以保证SQL Thread每一次apply事务的时候,能及时更新自己的relay log info的信息,就足够了。在从库异常crash拉起后,只需要以relay log info覆盖到master info,然后让IO Thread重新拉取relay log即可。

so,master_info_repository存文件还是存表里,其实没所谓,不过设置为TABLE也是不错的选择,理由是强迫症或者为了统一称它们为“双TABLE”听起来很舒服。


MySQL 5.6 + GTID

走的协议不一样了,用的COM_BINLOG_DUMP_GTID,所以定位方式也不一样了。

  • gtid_mode = ON & MASTER_AUTO_POSITION = 0: 
    虽然真有DBA这样配,但手册里也没怎么提及这种配置,非主流不讨论。

  • gtid_mode = ON & MASTER_AUTO_POSITION = 1: 
    和file & pos的方式一致。

另外还有一种配置如下:

log_slave_updates(5.6开gtid必须打开此参数)
log_bin(5.6开gtid必须打开此参数)
relay_log_recovery = ON
sync_binlog = 1

如果按上述配置,relay_log_info_repository是FILE或者TABLE也就无所谓了,因为auto-position不依赖relay log info了,而是通过Executed_gtid_set去判断自己和主库的差异事务。 
细节可以阅读响应代码:

Rpl_slave.cc:request_dump()
Rpl_master.cc:com_binlog_dump_gtid()

sync_binlog=1是必备的,需要binlog安全落地。因为GTID信息依赖binlog,从库可以通过自己的binlog获取自身完整的Executed_gtid_set。

总之,如果非双1时,可能会有问题,有兴趣可瞟一眼Yoshinori大佬report的  
https://bugs.mysql.com/bug.php?id=70659

当然,上述所有的场景innodb_flush_log_at_trx_commit也记得设置为1。 
简言之: 
MASTER_AUTO_POSITION=1场景要依赖GTID信息,GTID信息依赖binlog。


安全配置建议

MySQL 5.5:

怎么配置都没有囊括100%的crash场景,极端情况下仍然会crash unsafe。

如果愿意牺牲性能,保证较高的安全性可以这样设置。

sync_relay_log_info = 1
relay_log_recovery = ON


MySQL 5.6:

  • 单线程复制限定。 

  • 存储引擎限定:仅支持InnoDB,(TokuDB、RocksDB也应该可)。 

  • 主库:双1  

  • 从库(开不开MASTER_AUTO_POSITION通用):

relay_log_info_repository = TABLE
relay_log_recovery = ON
-- 即便是从库同样也请双1:
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1

如果5.6开启了MTS,先不讨论,官方MySQL 5.6 MTS那时还算个玩具。 
下篇见,直接讨论MySQL 5.7 MTS,那样的场景下将复杂一点点。


以上是关于MySQL#复制 - crash-safe Replication - 上的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 的 crash-safe 原理解析

MySQL 的 crash-safe 原理解析

crash-safe

MySQL crash-safe replication: MySQL的Crash Safe和Binlog的关系

请教ogg中复制进程rep01延迟问题

mysql 5.7主从库复制设置