【mysql】MySQL 中 Redo 日志与 Binlog 日志顺序一致性问题


相关文章:
redo log日志是什么?有啥用?
详细分析MySQL事务日志(redo log和undo log的区别)
MYSQL的日志(redo log,binlog)顺序读写,数据文件随机读写以及linux底层原理
MySQL 中Redo日志与Binlog日志顺序一致性问题

前言

MySQL中有六种日志文件,
分别是:重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog)、错误日志(errorlog)、慢查询日志(slow query log)、一般查询日志(general log),中继日志(relay log)。
其中重做日志和回滚日志与事务操作息息相关,二进制日志也与事务操作有一定的关系,这三种日志,对理解MySQL中的事务操作有着重要的意义。

首先,我们知道在MySQL中,二进制日志(binlog)是server层的,主要用来做主从复制和即时点恢复时使用的。而事务日志(redo log)是InnoDB存储引擎层的,用来保证事务安全的。现在我们来讨论一下MySQL主从复制过程中的一些细节问题,有关于主从复制可以看具体的章节。

在了解了以上基础的内容后,我们可以带着以下的几个问题去学习复制到底是怎样工作的。

  • 为什么MySQL有binlog,还有redo log?
  • 事务是如何提交的?事务提交先写binlog还是redo log?如何保证这两部分的日志做到顺序一致性?
  • 为了保障主从复制安全,故障恢复是如何做的?
  • 为什么需要保证二进制日志的写入顺序和InnoDB层事务提交顺序一致性呢?

简单总结二者关系:

  • redolog用来恢复当前机器上的存储引擎中的数据,例如innodb
  • binlog用来做复制同步的,让别的机器上的实例进行数据库操作

1. 为什么MySQL有binlog,还有redo log?

这个是因为MySQL体系结构的原因,MySQL是多存储引擎的,不管使用那种存储引擎,都会有binlog,而不一定有redo log,简单的说,binlog是MySQL Server层的,redo log是InnoDB层的。
在这里插入图片描述

redo log
在MySQL InnoDB中,redo log是用来实现事务的持久性,即当事务在提交时,必须先将该事务的所有操作日志写到磁盘上的 redo log file进行持久化,这也就是我们常说的 Write Ahead Log 策略。有了redo log,在数据库发生宕机时,即使内存中的数据还没来得及持久化到磁盘上,我们也可以通过redo log完成数据的恢复,这样就避免了数据的丢失。

binlog
在MySQL中,binlog记录了数据库系统所有的更新操作,主要是用来实现数据恢复主从复制的。一方面,主从配置的MySQL集群可以利用binlog将主库中的更新操作传递到从库中,以此来实现主从数据的一致性;另一方面,数据库还可以利用binlog来进行数据的恢复。

2. 事务是如何提交的?事务提交先写binlog还是redo log?如何保证这两部分的日志做到顺序一致性?

MySQL Binary log在MySQL 5.1版本后推出主要用于主备复制的搭建,我们回顾下MySQL在开启/关闭Binary Log功能时是如何工作的。

2.1 MySQL没有开启Binary log的情况下

正式环境一般是开启的,因此,可以先跳过本章节

首先看一下什么是CrashSafe?CrashSafe指MySQL服务器宕机重启后,能够保证:

  • 所有已经提交的事务的数据仍然存在。

  • 所有没有提交的事务的数据自动回滚。

Innodb通过Redo Log和Undo Log可以保证以上两点。为了保证严格的CrashSafe,必须要在每个事务提交的时候,将Redo Log写入硬件存储。这样做会牺牲一些性能,但是可靠性最好。为了平衡两者,InnoDB提供了一个innodb_flush_log_at_trx_commit系统变量,用户可以根据应用的需求自行调整。

innodb_flush_log_at_trx_commit = 0|1|2

0 – 每N秒将Redo Log Buffer的记录写入Redo Log文件,并且将文件刷入硬件存储1次。N由innodb_flush_log_at_timeout控制。

1 – 每个事务提交时,将记录从Redo Log Buffer写入Redo Log文件,并且将文件刷入硬件存储。

2 – 每个事务提交时,仅将记录从Redo Log Buffer写入Redo Log文件。Redo Log何时刷入硬件存储由操作系统和innodb_flush_log_at_timeout决定。这个选项可以保证在MySQL宕机,而操作系统正常工作时,数据的完整性。

通过redo日志将所有已经在存储引擎内部提交的事务应用redo log恢复,所有已经prepare但是没有commit的transactions将会应用undo log做rollback。然后客户端连接时就能看到已经提交的数据存在数据库内,未提交被回滚地数据需要重新执行。

2.2 MySQL开启Binary log的情况下?

MySQL引入二阶段提交(two phase commit or 2pc),MySQL内部会自动将普通事务当做一个XA事务(内部分布式事务)来处理:

本章节提到的两段式提交时MySQL 5.6以前的实现方式,但是此过程存在严重缺陷:此过程中为了保证MySQL Server层binlog的写入顺序和InnoDB层的事务提交顺序是一致的,MySQL数据库内部使用了prepare_commit_mutex这个锁。但是在启用了这个锁之后,并不能并发写入binlog,从而导致了group commit失效。这个问题在MySQL 5.6中的Binary Log Group Commit(BLGC)得到解决。

先写binlog还是先写redo log的呢?
针对这个疑问,我们可以做出两个假设。

假设一:先写redo log再写binlog
想象一下,如果数据库系统在写完一个事务的redo log时发生crash,而此时这个事务的binlog还没有持久化。在数据库恢复后,主库会根据redo log中去完成此事务的重做,主库中就有这个事务的数据。但是,由于此事务并没有产生binlog,即使主库恢复后,关于此事务的数据修改也不会同步到从库上,这样就产生了主从不一致的错误。

假设二:先写binlog再写redo log
想象一下,如果数据库系统在写完一个事务的binlog时发生crash,而此时这个事务的redo log还没有持久化,或者说此事务的redo log还没记录完(至少没有记录commit log)。在数据库恢复后,从库会根据主库中记录的binlog去回放此事务的数据修改。但是,由于此事务并没有产生完整提交的redo log,主库在恢复后会回滚该事务,这样也会产生主从不一致的错误。

通过上面的假设和分析,我们可以看出,不管是先写redo log还是先写binlog,都有可能会产生主从不一致的错误,那么MySQL又是怎么做到binlog和redo log的一致性的呢?

在MySQL内部,在事务提交时利用两阶段提交(内部XA的两阶段提交)很好地解决了上面提到的binlog和redo log的一致性问题:

  • 第一阶段: InnoDB Prepare阶段。此时SQL已经成功执行,并生成事务ID(xid)信息及redo和undo的内存日志。此阶段InnoDB会写事务的redo log,但要注意的是,此时redo log只是记录了事务的所有操作日志,并没有记录提交(commit)日志,因此事务此时的状态为Prepare。此阶段对binlog不会有任何操作。
  • 第二阶段:commit 阶段,这个阶段又分成两个步骤。第一步写binlog(先调用write()将binlog内存日志数据写入文件系统缓存,再调用fsync()将binlog文件系统缓存日志数据永久写入磁盘);第二步完成事务的提交(commit),此时在redo log中记录此事务的提交日志(增加commit 标签)。 还要注意的是,在这个过程中是以第二阶段中binlog的写入与否作为事务是否成功提交的标志。第一步写binlog完成 ,第二步redo log的commit未提交未完成,也算完成。

可以看出,此过程中是先写redo log再写binlog的。但需要注意的是,在第一阶段并没有记录完整的redo log(不包含事务的commit标签),而是在第二阶段记录完binlog后再写入redo log的commit 标签。

当innodb的redolog记录了,但是状态为Prepare时,尚未commit时,用户是查询不到该数据的!

此时的崩溃恢复过程如下:

  • 如果数据库在记录此事务的binlog之前和过程中发生crash,那么没有此条redoLog记录,并且后续的binlog也不会记录。数据库在恢复后认为此事务并没有成功提交,则会回滚此事务的操作。与此同时,因为在binlog中也没有此事务的记录,所以从库也不会有此事务的数据修改。

  • 如果数据库在记录此事务的binlog之后发生crash,此时redoLog新增一条,binlog新增一条,但是redolog的commit标识为false。此时,即使是redo log中还没有记录此事务的commit 标签,数据库在恢复后也会认为此事务提交成功

    因为在上述两阶段过程中,binlog写入成功就认为事务成功提交了。

    原理:数据库恢复后它会扫描最后一个redoLog文件,并提取其中的事务ID(xid),InnoDB会将那些状态为Prepare的事务(redo log没有记录commit 标签)的xid和Binlog中提取的xid做比较,如果在Binlog中存在,则重新提交该事务,否则回滚该事务。这也就是说,binlog中记录的事务,在恢复时都会被认为是已提交事务,会在redo log中重新写入commit标志,并完成此事务的重做(主库中有此事务的数据修改),在当前机器的innodb实例上补做事务的提交标识。与此同时,因为在binlog中已经有了此事务的记录,所有从库也会有此事务的数据修改。

    简单来说,即使是redo log中还没有记录此事务的commit 标签,只要记录了binlog,系统重启后,就会去扫描binlog,重新补上redo log的 commit

知识点总结:

  • 自动为每个事务分配一个唯一的ID(XID)。这个id很重要,是两阶段提交的核心

  • COMMIT会被自动的分成Prepare和Commit两个阶段。

  • Binlog会被当做事务协调者(Transaction Coordinator),Binlog Event会被当做协调者日志。

想了解2PC,可以参考文档:https://en.wikipedia.org/wiki/Two-phase_commit_protocol

Binlog在2PC中充当了事务的协调者(Transaction Coordinator)。由Binlog来通知InnoDB引擎来执行prepare,commit或者rollback的步骤。事务提交的整个过程如下:

在这里插入图片描述

  • engine是存储引擎实例,这里指innodb,第一个箭头处表示准备阶段,第二个箭头表示二次提交阶段
  • server是server层

如上图绿色显示,只要步骤执行到写binlog时,就算作提交了,当然,最后redo log 的commit算是锦上添花,即使局部失败,也不影响整体的成功

以上的图片中可以看到,事务的提交主要分为两个主要步骤:

  1. InnoDB Prepare准备阶段(Storage Engine(InnoDB) Transaction Prepare Phase)

此时SQL已经成功执行,并生成xid信息及redo和undo的内存日志。然后调用prepare方法完成第一阶段,papare方法实际上什么也没做,将事务状态设为TRX_PREPARED,并将redo log刷磁盘。

  1. commit提交阶段(Storage Engine(InnoDB)Commit Phase)

    • 记录协调者日志,即Binlog日志。
      如果事务涉及的所有存储引擎的prepare都执行成功,则调用TC_LOG_BINLOG::log_xid方法将SQL语句写到binlog(write()将binary log内存日志数据写入文件系统缓存,fsync()将binary log文件系统缓存日志数据永久写入磁盘)。此时,事务已经算作要提交了
    • 告诉引擎做commit。
      最后,调用引擎的commit完成事务的提交。会清除undo信息,刷redo日志(写入redo log的commit 标签),将事务设为TRX_NOT_STARTED状态。

PS:记录Binlog是在InnoDB引擎Prepare(即Redo Log写入磁盘)之后,这点至关重要。

通过上述MySQL内部XA的两阶段提交就可以解决binlog和redo log的一致性问题。数据库在上述任何阶段crash,主从库都不会产生不一致的错误。

简单来说,当系统挂掉,redo log里面可能包含无效数据(待回滚数据),当系统再恢复后,首先会去处理binlog,根据binlog会回滚redo log(处理完后,redo log的数据都是有效数据),最后才会读取redo log恢复 bufferpool。




参考:
《MySQL 中Redo与Binlog顺序一致性问题》参考的主体
《MySQL中binlog和redo log的一致性问题》

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值