当前位置 博文首页 > *IT界农民工*:Mysql 中写操作时保驾护航的三兄弟!
这期的文章主要是讲述写操作过程中涉及到的三个日志文件,看过前几期的话可能你或多或少已经有些了解了(或者从别的地方也了解过)。比如整个写操作过程中用到的两阶段提交,又或者是操作过程中涉及到的日志文件,但是总体来说不是很系统更谈不上全面。
今天我们就来会会这三兄弟。
图注:思维导图
这个名词你应该听到过很多次了,在这里再介绍下这位老朋友。
所谓的两阶段提交,从字面意思来看应该是有两个步骤来进行约束的。事实上也是如此。这两个步骤中的主角就是我们今天要讲的重要角色中的两位:binlog 和 redo log。
提到两阶段提交,SQL 语句的执行流程就绕不过去了。没辙,虽然提了很多遍,但还得再拉出来溜溜。只不过这次的侧重点和前面的会有些不同。
具体到操作流程上是这样的:
当执行某个写操作的 SQL 时,引擎将这行数据更新到内存的同时把对应的操作记录到 redo log 里面,然后处于 prepare 状态。并把完成信息告知给执行器。
执行器生成对应操作的 binlog,并把 binlog 写入磁盘里。然后调用引擎的提交事务接口,变更 redo log 状态为 commit,这样操作就算完成了。
好了,知道了两阶段提交后,我们接下来看看这些日志文件的真面目。
首先出场的是位于存储引擎层的 redo log,它是用来记录在"数据页做了什么修改"的物理日志文件。
提到 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 文件中的。
至于写入的时机,则由下面的参数来控制的:
(图片源自网络)
知道了写入时机,这里简单介绍下写入的方式吧。在 Innodb 中,redo log 的大小是固定的,那么就只能是以循环的方式进行写入了。假如当前我有 4 个文件,从第一个文件开始写入,直到最后一个文件写满为止,再回到开头将数据同步至文件后擦除掉继续写。
图中的 write pos 表示当前记录的位置,随着不断写入逐渐后移。当写到 ib_logfile_3号文件时,整个 redo log 就被写满了。此时更新操作就会被阻塞。系统根据 check point 标记位来擦除掉一些记录(当然前提是把这些记录同步至磁盘)。
总得来看 redo log 的写入方式就是一个不断写入,写满后擦除,又写入的过程。
说完了 redo log,我们再来看看另一个位于服务层的二进制日志文件 binlog,这位大兄弟扮演的角色是存储逻辑日志的,所谓的逻辑日志就是指修改了什么,都会记录其中。
例如:对 id = 1 的字段进行更新操作。
当然除了记录操作过程外,它还有支持主从同步及数据异常恢复的能力。
binlog 中有三种写入模式,我们分别来看下有什么不同及对应的优缺点:
(图片源自网络)
与 redo log 循环写不同的是, binlog 采用追加的方式写入,当一个文件写到一定大小后就会切换到另一个。
在上面的两阶段提交里我们有提到过在写入binlog 后会调用引擎的提交事务接口,变更 redo log 状态为 commit。那么它是如何找到对应的记录,或者换句话说,它们两者是怎么关联起来的呢?
答案是通过一个共同的字段 XID,不仅在事务提交时,在崩溃恢复的时候如果遇到仅写入 prepare 而没有 commit 的 redo log,也可以通过 XID 去寻找对应的事务。
到这里我们有必要回顾下写流程的操作,以更新某个字段为例:
到这里,你可能会疑惑了,通篇里哪有 undo log 的影子,你个渣男!
别急,来了!
根据字面意思,你应该能猜出来它是干啥的。回滚嘛,也就是给你一次后悔的机会。在进行数据修改时,同时记录 undo log,即同时记录相反操作的逻辑日志。你可以理解为操作 update 的时候,写一条对应相反的 update 记录,操作 delete 的时候,写一条对应的 insert 记录。
当事务回滚时。从 undo log 中读取到对应的逻辑记录就可以进行回滚操作了。
两阶段提交
两阶段提交过程中,更新内存的同时把对应操作记录到 redo log 中,并把生成的binlog 写入磁盘后提交事务。
重做日志
redo log 是位于存储引擎层的物理日志,用来记录在“数据页做了什么修改”的物理日志文件。采用循环写的方式,记录数据被修改后的样子。同时还提供数据恢复的能力。
二进制日志
binlog是位于服务层的逻辑日志,用来记录“对数据做了什么修改”的日志文件。与 redo log 不同的是,可以一直进行追加写入。同时还提供主从同步及数据异常恢复的能力。
回滚日志
在数据修改时,同时记录 undo log,可以确保在事务回滚操作时进行数据还原。
作者:大家好,我是莱乌,BAT搬砖工一枚。从小公司进入大厂,一路走来收获良多,想将这些经验分享给有需要的人,因此创建了公众号【IT界农民工】。定时更新,希望能帮助到你。同时,我给大家肝了一份Redis面经手册,在我的公众号内回复【pdf】即可获取,希望对你有所帮助。