mysql多线程复制crash_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”。

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

3135a188c553fa7e0c76d90bccf0796a.png

关于写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 | FILErelay_log_info_repository = TABLE | FILE

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

支持事务更新relay log info的过程就是:START TRANSACTION;apply log event;UPDATE mysql.slave_relay_log_infoSET 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 = TABLErelay_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 = ONsync_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 = 1relay_log_recovery = ON

MySQL 5.6:单线程复制限定。

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

主库:双1

从库(开不开MASTER_AUTO_POSITION通用):relay_log_info_repository = TABLErelay_log_recovery = ON-- 即便是从库同样也请双1:sync_binlog = 1innodb_flush_log_at_trx_commit = 1

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值