(3)redolog和binlog的区别与联系

Table of Contents

一、redo log的概念

二、binlog与redolog的区别

三、两阶段提交


在专栏一的change buffer和redo log的联系中,虽然我们更新数据时只更新内存的change buffer,但是在事务提交的时候,我们把 change buffer 的操作也记录到 redo log 里了,所以崩溃恢复的时候, changebuffer 也能找回来。这就是redo log在崩溃恢复中的作用。

我们用到了WAL技术, WAL 的全称是 Write-Ahead Logging ,它的关键点就是先写日志,再写磁盘,保证持久化到磁盘

WAL(Write-Ahead Logging)是一种实现事务日志的标准方法,具体而言就是:

1、修改记录前,一定要先写日志;

2、事务提交过程中,一定要保证日志先落盘,才能算事务提交完成。

 

一、redo log的概念

change buffer 的操作也记录到 redo log 里,并更新内存,这时候更新就算完成了,之后,引擎会在适当的时候(系统空闲时候),将这个操作记录更新到磁盘里面。

然而如果更新是在太多,redolog有可能写满了。 InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是1GB ,那么这块 日志模块总共就可以记录 4GB 的操作。写满了之后就要考虑擦除工作。

(3)redolog和binlog的区别与联系

write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头(循环写)。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。

write pos 和 checkpoint 之间的是空着的部分(例如上图的file0),可以用来记录新的更新操作。如果 write pos追上 checkpoint(可记录的空间满了) ,不能再执行新的更新,需要先擦除掉一些记录,把checkpoint推进一下。

只要更新记录写在了redolog上,即使数据库突然崩溃几天,回复后仍然可以通过这些记录执行更新操作(innodb可以保证及时数据库发生异常重启,之前提交的记录都不会丢失)。

二、binlog与redolog的区别

redo log 是InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog (归档日志)

这两种日志有以下三点不同:
            1. redo log 是 InnoDB 引擎特有的; binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用,MySQL数据库中的任何存储引擎对于数据库的更改都会产生binlog。
            2. redo log 是物理日志,记录的是 “ 在某个数据页上做了什么修改 ” ; binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如 “ 给 ID=2 这一行的 c 字段加 1 ”。

binlog 记录的都是事务操作内容,binlog 有三种模式:Statement(基于 SQL 语句的复制)、Row(基于行的复制) 以及 Mixed(混合模式)。具体这三种模式的区别请看主从同步和主备同步专栏


            3. redo log 是循环写的(类似一个循环队列),因为它的空间固定会用完; binlog 是可以追加写入的。 “ 追加写 ” 是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

            4.redo在 事务执行过程中 会不断的写入,而 binlog 是在 事务最终提交前 写入的

三、两阶段提交

为了让两份日志之间的逻辑一致, redo log 的写入拆成了两个步骤: prepare 和commit ,这就是 " 两阶段提交 "。要注意:

所谓的 redo log prepare ,是 “ 当前事务提交 ” 的一个阶段,也就是说,在事务 A 提交的时候,我们才会走到事务 A 的 redo log prepare 这个阶段。

由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写binlog ,或者采用反过来的顺序,会产生日志与数据不一致的问题,例如

  • 先写 redo log 后写 binlog,假设在 redo log 写完, binlog 还没有写完的时候,MySQL进程异常重启,redo log写完后,仍能够把数据恢复回来。但是binlog就没有记录这条语句,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,会导致数据不一致
  • 先写 binlog 后写 redo log 。如果在 binlog 写完之后 crash ,由于 redo log 还没写,崩溃恢复以后这个事务无效,单用binglog来恢复时就多了一个事务出来

通过一种特殊的xid,可以在两阶段提交的不同时刻发生异常重启时,将redo和binlog联系起来

(3)redolog和binlog的区别与联系

时序上 redo log 先 prepare , 再写 binlog ,最后再把 redo log commit。

具体而言,崩溃恢复在两阶段提交不同时刻的判断规则如下:图中浅绿色在Server层,深绿色位于innodb层

1.  如果 redo log 里面的事务是完整的,也就是已经有了 commit 标识,则直接提交;

2.如果 redo log 里面的事务只有完整的 prepare ,则判断对应的事务 binlog 是否存在并完整:通过xid来联系起来

a.  如果是,则提交事务;

如果把 innodb_flush_log_at_trx_commit 设置成 1 ,那么 redo log 在 prepare 阶段就要持久化一次,因为这个崩溃恢复逻辑是要依赖于prepare  的 redo log ,再加上 binlog 来恢复的

 


b.  否则,回滚事务。

时刻 A 的地方,也就是写入 redo log  处于 prepare 阶段之后、写 binlog 之前,发生了崩溃( crash ),由于此时 binlog 还没写, redo log 也还没提交,所以崩溃恢复的时候,这个事务会回滚。这时候, binlog 还没写,所以也不会传到备库。

在时刻 B ,也就是 binlog 写完, redo log 还没 commit 前发生crash ,此时崩溃对应的是2(a)的情况,崩溃恢复过程中事务还是会被提交

binlog 没有崩溃恢复的能力(MySQL 的原生引擎是MyISAM ,设计之初就有没有支持崩溃恢复)

所以只从崩溃恢复的角度来讲,可以吧binlog关掉,这样就不需要两阶段提交了,仍然系统为crash-safe的

所以为什么在正式的生产库上, binlog 都是开着的?为什么一定要有两阶段提交

  • redo log 是循环写,历史日志没法保留,起不到归档的作用
  • MySQL 系统依赖于 binlog,binlog 作为 MySQL 一开始就有的功能,被用在了很多地方

四、几个问题

追问 1 : MySQL 怎么知道 binlog 是完整的 ?
回答:一个事务的 binlog 是有完整格式的:

  • statement 格式的 binlog ,最后会有 COMMIT ;
  • row 格式的 binlog ,最后会有一个 XID event 。

另外,在 MySQL 5.6.2 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性

追问 2 : redo log  和 binlog 是怎么关联起来的 ?
回答:它们有一个共同的数据字段,叫 XID 。崩溃恢复的时候,会按顺序扫描 redo log :

  • 如果碰到既有 prepare 、又有 commit 的 redo log ,就直接提交;
  • 如果碰到只有 parepare 、而没有 commit 的 redo log ,就拿着 XID 去 binlog 找对应的事务。判断binlog是否完整

追问 3 :处于 prepare 阶段的 redo log 加上完整 binlog ,重启就能恢复, MySQL 为什么要这么设计 ?
回答:其实,这个问题还是跟我们在反证法中说到的数据与备份的一致性有关。

 binlog 写完以后 MySQL 发生崩溃,这时候 binlog 已经写入了,之后就会被从库(或者用这个binlog 恢复出来的库)使用。
所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性。