Mysql 中写操作时保驾护航的三兄弟!

时间:2022-05-07 22:33:12
Mysql 中写操作时保驾护航的三兄弟!

这期的文章主要是讲述写操作过程中涉及到的三个日志文件,看过前几期的话可能你或多或少已经有些了解了(或者从别的地方也了解过)。比如整个写操作过程中用到的两阶段提交,又或者是操作过程中涉及到的日志文件,但是总体来说不是很系统更谈不上全面。

今天我们就来会会这三兄弟。

Mysql 中写操作时保驾护航的三兄弟!

图注:思维导图

两阶段提交

这个名词你应该听到过很多次了,在这里再介绍下这位老朋友。

所谓的两阶段提交,从字面意思来看应该是有两个步骤来进行约束的。事实上也是如此。这两个步骤中的主角就是我们今天要讲的重要角色中的两位:binlog 和 redo log

提到两阶段提交,SQL 语句的执行流程就绕不过去了。没辙,虽然提了很多遍,但还得再拉出来溜溜。只不过这次的侧重点和前面的会有些不同。

具体到操作流程上是这样的:

当执行某个写操作的 SQL 时,引擎将这行数据更新到内存的同时把对应的操作记录到 redo log 里面,然后处于 prepare 状态。并把完成信息告知给执行器。

执行器生成对应操作的 binlog,并把 binlog 写入磁盘里。然后调用引擎的提交事务接口,变更 redo log 状态为 commit,这样操作就算完成了。

Mysql 中写操作时保驾护航的三兄弟!

好了,知道了两阶段提交后,我们接下来看看这些日志文件的真面目。

重做日志(redo log)

首先出场的是位于存储引擎层的 redo log,它是用来记录在"数据页做了什么修改"的物理日志文件。

WAL 技术

提到 redo log,WAL 技术必然是绕不过去的,全称是 Write-Ahead Logging。也就是在同步磁盘前先写日志,然后系统再根据一定的策略将日志里的记录同步到磁盘里。

存在的必要性

从上边的两阶段提交的过程里,我们可以看到 WAL 技术的使用场景。不知道你有没有疑惑,为什么中间非要写 redo log,直接将更新结果同步到磁盘里不行吗?傻孩子,同步到磁盘里就意味着每次写操作就得产生随机写盘操作,速度得多慢啊。

机智的你可能会说了,那我能不能一定的时间后从内存再同步到磁盘里,这种方式不行吗?来,先给你个脑瓜崩,你想想,我服务重启了,这些数据还在不?内存是易失的,不知道什么异常情况就会导致数据丢失。所以这时候就需要一个能持久化的中间文件,起到"缓冲"的作用,并且写入速度还不慢。

那么 redo log 就应运而生了。虽然同样存储在磁盘上,但是顺序写入在速度上并不受影响(疑惑的同学可以了解下磁盘的随机与顺序读写的区别)。

当然 redo log 除了能起到"延迟"同步磁盘文件的作用外,在数据库服务器宕机时,还可以用来恢复数据。

写入时机

谈到写入时机,是不是更疑惑了,难倒不是更新完内存就写入 redo log 文件吗?答案确实不是,因为中间还有一个 redo log buffer(内存中) 。Mysql 每执行一条语句,会先将记录写入 redo log buffer,后续执行 commit 操作时会以一定的时机写入到 redo log 文件(磁盘上)中。

值得注意的是,redo log buffer 里的数据是在执行 commit 操作时写入到 redo log 文件中的。

至于写入的时机,则由下面的参数来控制的:

Mysql 中写操作时保驾护航的三兄弟! Mysql 中写操作时保驾护航的三兄弟!

Mysql 中写操作时保驾护航的三兄弟!

(图片源自网络)

写入方式

知道了写入时机,这里简单介绍下写入的方式吧。在 Innodb 中,redo log 的大小是固定的,那么就只能是以循环的方式进行写入了。假如当前我有 4 个文件,从第一个文件开始写入,直到最后一个文件写满为止,再回到开头将数据同步至文件后擦除掉继续写。

Mysql 中写操作时保驾护航的三兄弟!

图中的 write pos 表示当前记录的位置,随着不断写入逐渐后移。当写到 ib_logfile_3号文件时,整个 redo log 就被写满了。此时更新操作就会被阻塞。系统根据 check point 标记位来擦除掉一些记录(当然前提是把这些记录同步至磁盘)。

总得来看 redo log 的写入方式就是一个不断写入,写满后擦除,又写入的过程。

二进制日志(binlog)

说完了 redo log,我们再来看看另一个位于服务层的二进制日志文件 binlog,这位大兄弟扮演的角色是存储逻辑日志的,所谓的逻辑日志就是指修改了什么,都会记录其中。

例如:对 id = 1 的字段进行更新操作。

当然除了记录操作过程外,它还有支持主从同步数据异常恢复的能力。

写入模式

binlog 中有三种写入模式,我们分别来看下有什么不同及对应的优缺点:

Mysql 中写操作时保驾护航的三兄弟!

(图片源自网络)

写入方式

与 redo log 循环写不同的是, binlog 采用追加的方式写入,当一个文件写到一定大小后就会切换到另一个。

与 redo log 的关联

在上面的两阶段提交里我们有提到过在写入binlog 后会调用引擎的提交事务接口,变更 redo log 状态为 commit。那么它是如何找到对应的记录,或者换句话说,它们两者是怎么关联起来的呢?

答案是通过一个共同的字段 XID,不仅在事务提交时,在崩溃恢复的时候如果遇到仅写入 prepare 而没有 commit 的 redo log,也可以通过 XID 去寻找对应的事务。

回顾下写流程

到这里我们有必要回顾下写流程的操作,以更新某个字段为例:

Mysql 中写操作时保驾护航的三兄弟!

回滚日志(undo log)

到这里,你可能会疑惑了,通篇里哪有 undo log 的影子,你个渣男!

别急,来了!

根据字面意思,你应该能猜出来它是干啥的。回滚嘛,也就是给你一次后悔的机会。在进行数据修改时,同时记录 undo log,即同时记录相反操作的逻辑日志。你可以理解为操作 update 的时候,写一条对应相反的 update 记录,操作 delete 的时候,写一条对应的 insert 记录。

当事务回滚时。从 undo log 中读取到对应的逻辑记录就可以进行回滚操作了。

总结

两阶段提交

  • 两阶段提交过程中,更新内存的同时把对应操作记录到 redo log 中,并把生成的binlog 写入磁盘后提交事务。

重做日志 

  • redo log 是位于存储引擎层的物理日志,用来记录在“数据页做了什么修改”的物理日志文件。采用循环写的方式,记录数据被修改后的样子。同时还提供数据恢复的能力。

二进制日志

  • binlog是位于服务层的逻辑日志,用来记录“对数据做了什么修改”的日志文件。与 redo log 不同的是,可以一直进行追加写入。同时还提供主从同步及数据异常恢复的能力。

回滚日志 

  • 在数据修改时,同时记录 undo log,可以确保在事务回滚操作时进行数据还原。

关于作者

作者:大家好,我是莱乌,BAT搬砖工一枚。从小公司进入大厂,一路走来收获良多,想将这些经验分享给有需要的人,因此创建了公众号【IT界农民工】。定时更新,希望能帮助到你。同时,我给大家肝了一份Redis面经手册,在我的公众号内回复【pdf】即可获取,希望对你有所帮助。

Mysql 中写操作时保驾护航的三兄弟!