前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL#复制 - crash-safe Replication - 下

MySQL#复制 - crash-safe Replication - 下

作者头像
用户1278550
发布2020-11-03 15:09:32
9880
发布2020-11-03 15:09:32
举报
文章被收录于专栏:idbaidba

接【MySQL#复制 - crash-safe Replication - 上】,继续看5.7的。同样只考虑全事务引擎的情况,非事务引擎忽略。本篇将分析5.7版本下,支持单线程和MTS复制的crash-safe配置,同时将讨论传统file & pos与使用GTID + MASTER_AUTO_POSITION的场景。

MySQL 5.7 single-thread slave

在单线程复制的情况下,5.7和5.6开关GTID的crash-safe其实可以简单理解为“没有差别”:

  • gtid_mode = OFF,用file & pos方式复制: 和MySQL 5.6一致。(见上篇)
  • gtid_mode = ON,MASTER_AUTO_POSITION = 0: 继续不讨论了,比较异类和非主流,不谈。
  • gtid_mode = ON,MASTER_AUTO_POSITION = 1: 和MySQL 5.6一致。(见上篇)

如果就这样单线程复制场景讨论完了,也太敷衍了,所以,这里还要提一下MySQL 5.7 GTID引入的一个新特性: 在MySQL 5.6,从库开启GTID必须配置log_binlog_slave_updates是因为如果从库重启后,它自己没办法知道自己执行了哪些GTID。@@gtid_executed是一个内存值,没有持久化。当时的做法是通过持久化了的binlog获取对应GTID信息。

5.7这个新特性就舒服了,将GTID信息存入了mysql.gtid_executed表中,所以无需开启log_binlog_slave_updates也可以开启GTID。不开binlog的好处也很明显,在永远不会提升为主库的从库上,或者不用级联的从库上,可以免去不必要的存储和性能开销。

因为上面的特性,可以思考一下:目前版本,开启log_bin,且关闭log_slave_updates的情况下,mysql.gtid_executed是实时更新的,那么这时,是不是也可以保证crash前后的GTID准确呢?关于这个问题,放到后面一起说。

MySQL 5.7 MTS

好家伙,MTS环境下,就复杂亿点点。因为有多个worker线程和一个coordinator去做原本SQL Thread做的事情。

多个worker将自身apply events的进度记录在mysql.slave_worker_info中,但还好该表也为事务表。换言之:MTS环境中每次apply events的时候,都会更新mysql.slave_worker_info,而mysql.slave_relay_info在每次做MTS检查点时做更新,所以slave relay info变得不是实时的了(和单线程不同)。

关于MTS做checkpoint的内容,可以参考两个参数:

slave_checkpoint_group 
slave_checkpoint_period 

所以!————这里就会出现比单线程复制更麻烦问题了:

1)、MTS可能出现gap事务。即后提交的事务可能先做了apply,之前的事务可能还没有结束,如之前是一个大事务,这个好理解。

2)、Exec_Master_Log_Pos不准确。因为MTS不会时时刻刻更新relay log info。称它为Gap-free low-watermark。官方手册里现在叫做Source binary log position lag

上述这两个问题,可阅读手册: https://dev.mysql.com/doc/refman/5.7/en/replication-features-transaction-inconsistencies.html

(懒得复制可以扫码跳转)

这两个问题的成因如果不理解,可以阅读一下MTS的相关内容。翻手册、google、看八怪老师的《深入理解MySQL主从原理》的第19和第20讲也可。这块的内容,有空我也再复习整理一下。

有了上面的背景知识,接下来想一下怎么配置能保证crash safe。

  • gtid_mode = OFF,用file & pos方式复制: 先说结论,个人认为无论怎么配置,无法100%保证所有场景都支持crash safe(如部分OS级别故障)。

尽量安全的配置(供参考):

master_info_repository = TABLE
sync_master_info = 1
relay_log_info_repository = TABLE
relay_log_recovery = ON
sync_relay_log = 1 
slave_preserve_commit_order = ON 

这里涉及到一个知识点就是,MTS在file+pos的情况下是怎么做恢复的,这个过程我是参考八怪老师的《深入理解MySQL主从原理》的第25讲,细节也可以参考一下init_slave函数。

分析一下: 1)这个场景下,mysql.slave_master_info的信息不会被覆盖,所以master info需要保证可靠,所以需要放在表里,并且将sync_master_info设置为1。

关于sync_master_info参数: 手册里描述如下: master_info_repository = TABLE. If the value of sync_master_info is greater than 0, the replica updates its connection metadata repository table after every sync_master_info events. If it is 0, the table is never updated.

2)这里的第二个核心关键点是sync_relay_log参数要设置为1,因为MTS且未开MASTER_AUTO_POSITION的场景下,在crash后是需要依赖relay log去恢复Gap事务的,所以需要保证relay log的完整性。这样的配置首先性能受到很大影响,其次,如MySQL 5.5版本一样会存在安全问题,因为极端情况下写文件行文本身不靠谱,仍可能至少丢一个event。

读取relay log恢复调用栈:

Relay_log_info::rli_init_info()
 ->init_recovery() // relay_log_recovery打开
  ->mts_recovery_groups() 

总之,只要relay log丢失,就麻烦了,相关"bug": https://bugs.mysql.com/bug.php?id=81840

(懒得复制可以扫码跳转)

关于这个问题,其实有个叫Jean-François Gagné的老哥提出了一个“临时性”的解决方案(眼熟一下,后面还有这个大佬):

1) sync_relay_log = 1
2) slave_preserve_commit_order = ON 
(when slave_parallel_mode = LOGICAL_CLOCK)

这样设置,主要是为了消除Gap。slave_preserve_commit_order参数的功能如它自己的名字,细节可以翻一下手册。

至此,这个场景在上面的配置后,已经可以竭尽所能保证crash safe了,有点勉强,性能还不好。

引申内容

JFG这个老哥,同时也给官方提供了一个【改进建议】(现在MySQL版本没实现): To solve Bug#81840, we need to 1st download binlog and then to fix the relay log position in the mysql.slave_worker_info table. This is tedious, but not overly complicated.

这个建议的理论基础是:MTS更新mysql.slave_worker_info的行为,是实时可靠的。有了这个基础,“理论上”,就可以知道从哪些地方追补binlog了。

至少目前版本好像真没办法在这样的场景保证crash safe。换言之,如果基于relay log的恢复能够做好,也就是bug#81840及相关问题,官方能够搞定,以后在用MTS+非GTID场景下,也能保证了。

  • gtid_mode = ON,MASTER_AUTO_POSITION = 0: 杀马特式非主流配置,我们不跟它玩,依旧不谈。
  • gtid_mode = ON,MASTER_AUTO_POSITION = 1: 从上面可以知道,基于file+pos做crash后恢复,难点在于,恢复过程依赖relay log,而relay log的完整性难以保证,那么——

启用MASTER_AUTO_POSITION呢?那之前的问题好像都不是问题了。故这种场景就非常简单了:

各种原因crash之后,比较主和从库的GTID信息似乎就好了。这个过程大致如下:

因为依赖GTID,所以只需要保证GTID信息没问题就行了。那,依赖relay log补Gap也是不是没有意义了?——的确,DBA们是这样想的,但一开始MySQL没这样做,后来官方的复制团队改了(不了解契机,但好像是被怼了之后改的):

比如在宕机前从库的Executed_Gtid_Set$server_uuid:1-100:102-105:108-120,那么实际上只要拿到缺失GTID对应的event即可,主库binlog dump GTID线程只需要发送101、106-107就恢复完成了。

所以……这个过程可以不依赖relay log。同时,也不需要mysql.slave_worker_info的内容了。

安全可靠的配置就更简单了,如下:

-- 双1
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
relay_log_recovery = ON (5.7.28前需要配置这个)

对,特别简单,双1就完事,这样,终于不用依赖relay log了,所以也不需要保证relay log的完整性了。

5.7.28+/8.0.18+后: relay_log_recovery这个参数也变得无所谓了,真正的实现了通过GTID即可完整恢复。

相关版本和改进

不同版本MTS的recovery过程有差,所以大概整理了一下,参考如下:

  • 5.7.13-: 需要手动恢复MTS产生的gap事务,需要依赖relay log(请保证它完整),且有点麻烦,也是产生ERROR 1872的一个原因之一,这个之前写过。
  • 5.7.13 ~ 5.7.28: 可自动恢复MTS产生的Gap事务,但是,好像还是需要依赖relay log(依然需要它完整)。依旧僵硬。因为上面分析到,在GTID的MASTER_AUTO_POSITION场景下,理论上是不需要依赖relay log的。

于是JFG老哥又去找官方battle了,细节可以看这个链接: https://bugs.mysql.com/bug.php?id=92882

(懒得复制可以扫码跳转)

他的意思如下: Do not fail relay-log-recovery in the case of vanished relay logs when GTID Auto_Position is enabled as the table mysql.slave_worker_info is not needed in this case.

  • 5.7.28+ / 8.0.18+: 官方说“哦哦哦,好的哥。”,然后修复了这个问题,在MASTER_AUTO_POSITION打开的情况下,跳过relay log恢复,通过GTID信息恢复。终于不需要保证relay log的完整性了。

对应的Changelog如下: On a multi-threaded slave with GTIDs in use and MASTER_AUTO_POSITION set to ON, following an unexpected halt the slave would attempt relay log recovery, which failed if relay logs had been lost, preventing replication from starting. However, this step was unnecessary as GTID auto-positioning can be used to restore any missing transactions. In a recovery situation, the slave now checks first whether MASTER_AUTO_POSITION is set to ON, and if it is, skips relay log recovery.

比较好理解,就不翻译了。

安全配置建议

总结一下如下:

  • 单线程: 送分题,如果不想复杂,和MySQL 5.6一样即可(参考上篇)。
  • MTS: 升级到MySQL 5.7.28+版本。非GTID下的传统复制并不能保证100%的可靠,而且性能不佳。建议打开GTID + MASTER_AUTO_POSITION,并保证双1。显然,GTID机制比file & pos的方式要更智能。 有一说一,GTID都诞生很多年了(好像有7、8年了),实在是比传统位点复制先进多了,为什么不用呢?

引申问题与推荐阅读

上面分析了开启MASTER_AUTO_POSITION时,无论单线程还是MTS,都需要依赖GTID,而GTID信息正确这个需求,得保证binlog完整。——那么从库关闭binlog时会可行吗?或者sync_binlog != 1时,能否保证crash safe?因为毕竟这样的场景是可以提升从库性能的。

从理论上讲,我感觉行,因为5.7后,GTID信息也存在mysql.gtid_executed表里持久化了。好像也可以不依赖binlog。

随手搜一下,果然还是Jean-François Gagné出手了,他提了一个类似的场景给官方。大概意思是想又要性能又可以满足安全: https://bugs.mysql.com/bug.php?id=92109

(懒得复制可以扫码跳转)

目前为止,官方给的回复是将该场景作为特性类,不属于bug类: I'm switching this to "Feature request"

有趣的是,底下还有赞同JFG老哥,并催进度的: Just reading all the articles of Jean-François and I think the solution is fairly easy and an important fix. Any idea about the current status of implementation?

MySQL社区真好玩儿。

另一个引申问题

在MTS环境中,且有Gap的时候,如果不启用GTID,各大备份工具(mysqldump\mydumper\xtrabackup... )是怎么处理这个问题的。

推荐阅读

google一下《Demystifying MySQL Replication Crash Safety》,这个就是上面一直在和官方斗智斗勇的JFG老哥在Percona Live 2018上分享的一个话题。如果有兴趣可看看。

其他几篇文章:

MySQL#复制 - crash-safe Replication - 上

MySQL#复制 - 原生复制的一致性探讨

【MySQL】浅谈ERROR 1872与5.6/5.7 MTS group recovery

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 yangyidba 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MySQL 5.7 single-thread slave
  • MySQL 5.7 MTS
    • 引申内容
    • 相关版本和改进
    • 安全配置建议
    • 引申问题与推荐阅读
      • 另一个引申问题
        • 推荐阅读
        相关产品与服务
        云数据库 SQL Server
        腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档