【MySQL】RedoLog日志和Binlog日志哪个先写的问题?

RedoLog日志和Binlog日志哪个先写的问题?

前言

MySQL中六种日志文件,分别是重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog)、错误日志(errorlog)、慢查询日志(slow query log)、一般查询日志(general log)、中继日志(relay log).其中重做日志和回滚日志与事务操作息息相关,二进制日志与事务操作有一定的关系,这三种日志,对理解MySQL的事务操作有着重要的意义。
首先我们知道在MySQL中,二进制日志(binlog)是server层的,主要用来做主从复制和即时点恢复时使用的。而事务日志(redolog)是InnoDB存储引擎层的,用来保证事务安全的。你可能会有这样的疑问。

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

简单总结二者关系:

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

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

在这里插入图片描述

这个是因为MySQL体系结构的原因,MySQL是多存储引擎的,不管使用哪种存储引擎,都会有binlog,而不一定有redolog,简单地说,binlog是MySQL Server层的,redo log是InnoDB层的。

  • redolog
    在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在MySQL5.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(控制的是Redo Log文件的写入)系统变量,用户可以根据应用的需求自行调整.

innodb_flush_log_at_trx_comit = 0|1|2 (默认是1)
0 - 每N秒将Redo Log Buffer的记录写入Redo Log 文件,并且将文件刷入硬件存储1次,N由innodb_flush_log_at_timeout控制。默认是1s
1 - 每个事务提交时,将记录从Redo Log Buffer写入Redo Log文件,并且将文件刷入硬件存储
2 - 每个事务提交时,仅将记录从Redo Log Buffer写入Redo Log文件,也就是写入到操作系统缓存,但不保证它们被同步到磁盘,操作系统可能会定期将缓存中的数据写入磁盘。这种设置在操作系统崩溃时提供了一定程度的持久性保证,但在MySQL数据库崩溃时可能会丢失数据

innodb_flush_log_at_timeout
在这里插入图片描述
innodb_flush_log_at_trx_commit
在这里插入图片描述

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

2.2MySQL开启Binary LOg的情况下?

MySQL引入二阶段提交(two phase commit or 2pc),MySQL内部会自动将普通事务当作一个XA事务(内部分布式事务)来处理:
(在这里提到的两段式提交是MySQL 5.6以前的实现方式,但是此过程存在严重缺陷:此过程为了保证MySQL Server层binlog的写入顺序和InnoDB层的事务提交顺序是一致的,MySQL数据库内部使用了prepare_commit_mutext这个锁。但是在启用了这个锁之后,并不能并发写入binlog,从而导致group commit失效,这个问题在MySQL 5.6中的Binary Log Group Commit(BLGC)得到解决)
先写binglog还是先写redolog的呢?
针对这个疑问,我们可以做出两个假设.

  • 1.假设一:先写redo log再写binlog
    想象以下,如果数据库系统在写完一个事务的redolog时发生crash,而此时这个事务的binlog还没有持久化。在数据库恢复后,主库会根据redolog中区完成此事务的重做,主库中就有这个事务的数据。但是,由于此事务并没有产生binlog,即时主库恢复后,关于此事务的数据修改也不会同步到从库上,这样就产生了主从不一致的错误
  • 2.假设而:先写binlog再写redo log
    想象以下,如果数据库系统在写完一个事务的binlog时发生crash,而此时这个事务的redo log还没有持久化,或者说此事务的redolog还没记录完(至少没有记录commitlog)。在数据库恢复后,从库会根据主库中记录的binlog去回放此事务的数据修改。但是,由于此事务并没有产生完整提交的redo log,主库在恢复后会回滚事务,这样也会产生主从不一致的错误。

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

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

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

可以看出,此过程中是先写redolog再写binlog的。但需要注意的是,在第一阶段并没有记录完整的redolog(不包含事务的commit标签),而是在第二阶段记录完binlog后再写入redolog的commit标签。
当innodb的redolog记录,但是状态为Prepare时,尚未commit时,用户是查询不到该数据的

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

  • 1.如果数据库在记录此事务的binlog之前和过程中发生crash那么没有此条redolog记录,并且后续的binlog也不会记录。数据库在恢复后认为此事务并没有成功提交,则会回滚此事务的操作。与此同时,因为在binlog中
    也没有此事务的记录,所以从库也不会有此事务的数据修改。
  • 2.如果数据库在记录此事务的binlog之后发生crash,此时redolog新增一条,binlog新增一条,但是redolog的commit标识为false.此时,即使是redo log中还没有记录此事务的commit标签,数据库在恢复后也会认为此事务提交成功.

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

知识点总结:

  • 1.自动为每个事务分配一个唯一的ID(XID)。这个ID很重要,是两阶段提交的核心
  • 2.COMMIT会被自动的分成Prepare和Commit两个阶段
  • 3.Binlog会被当作事务协调者(Transaction Coordinator) Binlog Event会被当作协调者日志

binlog在2PC中充当了事务的协调者(Transaction Coordinator)由binlog来通知InnoDB引擎来执行prepare,commit或者rollback的步骤。事务提交的整个过程如图所示:
在这里插入图片描述

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

如上图绿色显示,只要步骤执行到写binlog时,就算作提交了,当然,最后redolog的commit算是锦上添花,即使局部失败,也不影响整体的成功。上面的图片可以看到,事务的提交主要分为两个主要步骤:

  • 1.InnoDB Prepare准备阶段(Storage Engine (InnoDB) Transaction Prepare Phase)
    此时SQL已经成功执行,并生成xid信息及redo和undo的内存日志。然后调用prepare方法完成第一阶段,prepare方法实际上什么也没做,将事务状态设为TRX_PREPARED,并将redo log刷磁盘
  • 2.commit提交阶段(Storage Engine(InnoDB) Commit Phase)
    记录协调者日志,即binlog。如果事务设计的所有存储引擎的prepare都执行成功,则调用TC_LOG_BINLOG::log_xid方法将SQL语句写到binlog(write()将binnary log内存日志数据写入文件系统才能,fsync()将binnary log文件系统缓存日志数据永久写入磁盘)。此时,事务已经算作要提交了告诉引擎做commit.最后调用引擎的commit完成事务的提交。会清楚undo信息,刷redo日志(写入redo log 的commit标签),将事务设为TRX_NOT_STARTED状态。

PS:记录binlog是在InnoDB引擎Prepare(即Redo Log写入磁盘)之后,这点至关重要。通过上述MySQL内部XA的两阶段提交就可以解决binlog和redolog的一致性问题。数据库在上述任何阶段crash,主从库都不会产生不一致的错误。
简单来说,当系统挂掉,redo log里面可能包含无效数据(待回滚数据),当系统再恢复后,首先会处理binlog,根据binlog会回滚redolog(处理完后,redolog的数据都是有效数据),最后才会读取redolog恢复bufferpool

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coffee_babe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值