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

深入浅出MySQL crash-safe

作者头像
用户1278550
发布2019-04-09 11:20:35
9050
发布2019-04-09 11:20:35
举报
文章被收录于专栏:idbaidba

一 前言

MySQL主从架构已经被广泛应用,保障主从复制关系的稳定性是大家一直关注的焦点。MySQL 5.6针对主从复制稳定性提供了新特性:slave支持crash-safe。该功能可以解决之前版本中系统异常断电可能导致relay_log.info位点信息不准确的问题。本文将从原理,参数等几个方面对该特性进行介绍。

二 crash-unsafe

在了解slave crash-safe 之前,我们先分析MySQL 5.6之前的版本出现slave crash-unsafe 的原因。我们知道在一套主从结构体系中,slave包含两个线程:即IO thread和SQL thread。两个线程的执行进度(偏移量)都保存在文件中。

IO thread负责从master拉取binlog文件并保存到本地的relay-log 文件中。 SQL thread负责执行重复sql,执行relay-log记录的日志。

crash-unsafe情况下 SQL_thread 的 的工作模式:

代码语言:javascript
复制
START TRANSACTION;
 Statement 1
  ...
 Statement N
 COMMIT;

Update replication info files (master.info, relay_log.info)

IO thread的执行状态信息保存在master.info 文件, SQL thread的执行状态信息保存在 relay-log.info 文件。slave 运行正常的情况下,记录位点没有问题。但是每当系统发生crash,存储的偏移量可能是不准确的(需要注意的是这些文件被修改后不是同步写入磁盘的)。因为应用binlog和更新位点信息到文件不是原子操作,而是两个独立的步骤。比如 SQL thread已经应用relay-log.01的4个事务

代码语言:javascript
复制
 trx1(pos:10)
 trx2(pos:20)
 trx3(pos:30)
 trx4(pos:40) 

但是 SQL thread 更新位点(relay-log.01,30)到relay-log.info 文件中,slave实例重启的时候 sql thread 会重复执行事务 trx4,于是乎,大家就看到比较常见的复制报错 error 1062 ,error 1032 。

三 crash-safe 特性

3.1 保障apply log和更新位点信息操作的原子性

通过上面的分析,我们知道slave crash-unsafe的原因在于应用binlog和更新文件的非原子性。MySQL 5.6版本通过将更新位点信息存放到表中,并且和正常的事务一起执行,进而保障apply binlog的事务和更新relay info信息到slave_relay_log_info 的原子性.

就是把SQL thread执行事务和更新 mysql.slave_replay_log_info 的语句合并为同一个事务,由MySQL系统来保障事务的原子性。我们可以通过伪代码来模拟 crash-safe 的原理:crash-safe情况下 SQL_thread 的工作模式

代码语言:javascript
复制
START TRANSACTION;
  Statement 1
  ...
  Statement N
  Update replication info
COMMIT

一图胜千言:

绿色的代表实际业务的事务,蓝色的是开启MySQL执行的更新slave_replay_log_info 相关位点信息的sql ,然后将这两个sql合并在一个事务中执行,利用MySQL事务机制和InnoDB表保障原子性。不会出现应用binlog 和更新位点信息两个动作割裂导致不一致的问题。

3.2 crash 后的恢复动作

通过设置 relay_log_recovery = ON,slave 遇到异常crash,然后重启的时候,系统会删除现有的relay log,然后IO thread会从mysql.slave_replay_log_info 记录的位点信息重新拉取主库的binlog。MySQL如此设计的出发点是:

  1. SQL thread apply binlog的位点永远小于等于IO thread从主库拉取的位点。
  2. SQL thread记录的位点是已经执行并且提交的事务之后位点信息。

一图胜千言:

蓝色的update语句代表已经执行并提交的事务,绿色的delete 语句表示正在执行的sql,还未提交。此时slave_replay_log_info表记录的relay log info是update语句结束,delete语句开始之前的位点 (relay_log.01,100) 。如果遇到系统crash,slave实例重启之后,会删除已经有的relaylog,并且IO thread会从(relay_log.01,100)对应的master binlog位点重新拉取主库的binlog,SQL thread也会从这个位点开始应用binlog。

3.3 GTID 模式下的crash safe

和基于位点的复制不同,GTID 模式下使用新的复制协议 COM_BINLOG_DUMP_GTID 进行复制。举个?

实例a的事务集合set_a, 实例b的事务集合set_b ,设置b为a的从库的时候,其中的binlog协议伪算法如下:

  1. 实例b指向主库实例a, 基于主备协议建立主从关系
  2. 实例b将GTID信息发送给实例a UNION(@@global.gtid_executed, Retrieved_gtid_set - last_received_GTID)
  3. 实例a计算出set_b 与set_a的差集,也就是存在于set_a 但是不存在与set_b 的GTID集合,判断实例a本地的binlog是否包含了该差集所需要的所有binlog事务。 a 如果不包含,表示实例a已经把实例b需要的binlog删除了,直接返回报错。 b 如果确认全部包含 实例a从本地binlog文件里面,找到第一个不在set_b 的事务,发送给实例b
  4. 从这个事务开始,往后读文件,按顺序取binlog发送给实例b。

GTID 模式下,slave crash-safe 运行机制

蓝色ABC:3 表示已经执行并提交的事务,绿色ABC:4表示正在执行的事务,此时slave crash,实例记录的gtid_executed=ABC:1-3,系统重启relay_log被删除。slave 将 UNION(@@global.gtid_executed, null) 的计算结果也即是gtid_executed=ABC:1-3发送到主库,主库会将ABC:3以后的binlog传送给slave继续执行。

注意 从新的复制协议中slave重启时是基于binlog中的GTID信息进行复制的,并不依赖于mysql.slave_replay_log_info。为了保障binlog及时落盘slave要设置 双1模式 sync_binlog = 1和innodb_flush_log_at_trx_commit = 1

3.4 如何开启crash-safe 特性

通过配置两个如下两个参数开启该特性。

代码语言:javascript
复制
 relay_log_info_repository = TABLE
 relay_log_recovery = ON

看到这里是不是有疑问为什么没有master.info 相关的参数配置?

其实开启slave的crash-safe之后,slave重启的时候会自动清空之前的relay-log,IO thread从mysql.slave_relay_log_info表中记录的位点开始拉取数据,而不是依赖slave_master_info表相关数据。

注意: 如果是MySQL 5.6.5 或者更早期。slave_master_info 和 slave_relay_log_info 表默认使用MyISAM 引擎。所以还得修改成innodb,如下:

ALTER TABLE mysql.slave_master_info ENGINE=InnoDB; ALTER TABLE mysql.slave_relay_log_info ENGINE=InnoDB;

3.5 相关参数
  1. 开启crash-safe之后,slave 重启之后,不再依赖master info相关的参数,所以这两个参数不做过多讨论。不过为了和relay log info存储一致,推荐存储maste-info到表里,sync_master_info 保持默认,设置为比较低的值,在写压力比较大的情况下,会有IO损耗。 master_info_repository =TABLE sync_master_info=0
  2. 开启crash-safe 必要参数 relay_log_info_repository = TABLE relay_log_recovery = 1 这2个不多做介绍了,前面已经将的非常透彻。
  3. relay log 相关 当relay_log_info_repository=file时, 更新位点信息的频率依赖于sync_relay_log_info = N (N>=0): a 当sync_relay_log_info=0时,MySQL依赖OS系统定期更新。 b 当sync_relay_log_info=N时(N>0), MySQL server 会在每执行N个事务之后调用fdatasync()刷relay-log.info 文件。 当relay_log_info_repository=table 如果mysql.slave_relay_log_info是 innodb 存储引擎,则每次事务更新,系统会自动忽略sync_relay_log_info的设置。 如果mysql.slave_relay_log_info是非事务存储引擎,则 a 当sync_relay_log_info=0时,不更新。 b 当sync_relay_log_info=N时(N>0), MySQL server 会在每执行N个事务之后调用fdatasync()刷relay-log.info 文件。 sync_relay_log 控制着relay-log的刷新策略,类似sync_binlog。不过这个参数在开启crash-safe特性之后没有什么实质的意义。建议保持该参数为默认值即可

四 其他问题

每个硬币都有它的两面性。开启crash-safe会带来哪些潜在的问题?

1 重启slave,重新拉取relay-log,一主多从的集群会给主库带来IO和带宽压力。

2 主库不可用,或者binlog被删除了,slave找不到所需要的binlog。

五 参考 文章

[1] 图片来自 https://hackmongo.com/post/crash-safe-mysql-replication-a-visual-guide/

[2] http://dev.mysql.com/doc/refman/5.7/en/replication-solutions-unexpected-slave-halt.html

[3] http://dev.mysql.com/doc/refman/5.7/en/replication-solutions-unexpected-slave-halt.html

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一 前言
  • 二 crash-unsafe
  • 三 crash-safe 特性
    • 3.1 保障apply log和更新位点信息操作的原子性
      • 3.2 crash 后的恢复动作
        • 3.3 GTID 模式下的crash safe
          • 3.4 如何开启crash-safe 特性
            • 3.5 相关参数
            • 四 其他问题
            • 五 参考 文章
            相关产品与服务
            云数据库 SQL Server
            腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档