彻底搞懂MySQL 的 redo log 、 binlog、undo log

文章目录

前言

前面两篇文章介绍了 MySQL 的体系架构和 InnoDB 的存储结构和体系结构,今天这篇文章主要讲解 MySQL 中的一些重要 log 文件的作用。包括:redo log,binlog,undo log

宏观角度理解Mysql体系架构传送门

InnoDB体系架构详解传送门


前面的文章讲解了 MySQL 的体系架构,所以,我们知道一条 select 语句是如何执行的。如果是 update 语句呢,执行步骤和查询语句其实是一样的,在执行语句前要连接数据库,这是连接器的工作。如果查询缓存中存在这条 SQL 的结果集缓存,直接取出返回客户端,前面说过,表上有更新的时候,这个表相关的查询缓存都会失效,所以查询缓存不建议使用,在 MySQL 8.0 版本把查询缓存删除了。接下来,分析器、优化器、执行器分别做各自的工作,查询出结果集返回给客户端。

但 update 语句和 select 语句不一样的是,update 还涉及到两个重要的日志模块,也就是 redo log 和 bin log

redo log

反过来思考,如果不存在这两个日志,会出现什么问题?

update 操作其实是分为两步操作,先查询到对应的行记录,再根据条件进行更新操作。如果没有 redo log 的话,MySQL 每次的update操作都要更新磁盘文件,更新磁盘文件需要先在磁盘中找到对应的行记录,再更新,每一条 update 语句都要操作磁盘文件,整个过程的 I/O 成本,查找成本都很高。为了解决这个问题,InnoDB 引擎的设计者想到了一个办法,先将记录写到 redo log 中,并更新内存,这个时候更新就算完成了,操作内存比操作磁盘要快的多。同时,InnoDB 会在适当的时候,将 redo log 中的记录更新到磁盘文件中。这个更新往往是系统空闲时做。

每次更新操作都要往 redo log 中写入,如果 redo log 满了,空间不够用了怎么办?

InnoDB 的 redo log 文件是固定大小的,比如可以配置一组4个文件,每个文件大小是 1GB,那么 redo log 中可以记录 4GB 的操作,InnoDB 会从第一个文件开始写入,直到第四个文件写满了,又回到第一个文件开头循环写,如下图。

彻底搞懂MySQL 的 redo log 、 binlog、undo log
write pos是当前记录的位置,一边写一边后移,写到第3号文件末尾后就回到0号文件开头。checkpoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。

write pos和checkpoint之间的是 redo log上还空着的部分,可以用来记录新的操作。如果write pos追上checkpoint,表示 redo log 满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把checkpoint推进一下。

有了redo log,InnoDB就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-safe。


binlog

刚刚说的 redo log 是执行引擎层的 log 文件,我们都知道,MySQL 整体来看,分为 Server 层和引擎层,而 binlog 是 Server 层面的 log 文件,也就是所有执行引擎都有 binlog

那为什么 InnoDB 有一份 log 文件,MySQL 有一份 log 文件呢?

因为以前的MySQL没有InnoDB引擎,MySQL5.5前使用的 MyISAM引擎,但是 MyISAM 没有 crash-safe 的能力,而 binlog 只能用于归档。InnoDB 是后来作为 MySQL 的引擎以插件形式引入的。既然只靠 binlog 无法实现 crash-safe 的能力,所以 InnoDB 使用另一套日志系统——redo log 来实现。

redo log 与 binlog

  • redo log 是InnoDB 引擎特有的;而 binlog 是MySQL Server 层实现的
  • redo log 是物理日志,记录的是“在某个数据页做了什么修改”;而 binlog 是逻辑日志,记录的是语句的原始逻辑。比如 update T set c=c+1 where ID=2;这条SQL,redo log 中记录的是 :xx页号,xx偏移量的数据修改为xxx;binlog 中记录的是:id = 2 这一行的 c 字段 +1
  • redo log 是循环写的,固定空间会用完;binlog 可以追加写入,一个文件写满了会切换到下一个文件写,并不会覆盖之前的记录

update 操作流程

update T set c=c+1 where ID=2;

1、执行器先通过引擎查询到 id = 2 这行数据,id 是主键,直接遍历主键索引树直接插到这行数据,如果这行数据所在的数据页在内存中,就直接返回结果给执行器,否则,需要先从磁盘读入内存,然后再返回。

2、执行器拿到引擎给的行数据,把 这个值+1,得到新的一行数据,再调用引擎接口写入这行数据。

3、引擎将这行数据更新到内存中,同时记录到 redo log 中,此时 redo log 处于 perpare 状态,此时就告知执行器已经更新完成了,随时可以提交事务。

4、执行器生成这个操作的binlog,并把binlog写入磁盘

5、执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log改成提交(commit)状态,更新完成


如下图为 update 语句的执行流程,深色代表 MySQL 执行器中执行的,浅色代表 InnoDB 内部执行的。

彻底搞懂MySQL 的 redo log 、 binlog、undo log

两阶段提交

写入 redo log 分为两个步骤,prepare 和 commit,这就是“两阶段提交”。

为什么要有两阶段提交,就是为了让 redo log 和 binlog 两个文件保持一致。我们还是用反证法来说明,假设没有两阶段提交会发生什么问题:

  • 先写 redo log,再写 binlog,假设 redo log 写完,binlog 还没写完,MySQL 进程异常重启,redo log 写完后,即使系统崩溃,仍然能把数据恢复回来,所有恢复后的数据是正确的。但是 binlog 没写完,这时候 binlog 中就没有记录这条语句的操作,因此,之后备份日志的时候,binlog 就没有这条操作记录,如果用这个 binlog 来恢复临时库的话,由于这条语句记录的丢失,临时库就会少了这一个语句的操作,恢复出来的数据就与原库的值不同。
  • 先写 binlog,再写 redo log,如果在 binlog 写完后系统崩溃了,由于 redo log 还没写,崩溃后这个事务无效,所以磁盘数据文件中的数据是没有这条语句的操作的,但是 binlog 中已经做了记录,所以以后用这个 binlog 来做数据恢复时,就多了一个事务操作,与原库的数据不一致。

如果没有“两阶段提交”,会导致 redo log 和 binlog 记录的操作不一致,那么数据库的状态就有可能和用它的日志恢复出来的库数据不一致。

所以,能够保证 redo log 和 binlog 的操作记录一致的流程是,将操作先更新到内存,再写入 redo log,此时标记为 prepare 状态,再写入 binlog,此时再提交事务,将 redo log 标记为 commit 状态。

undo log

undo log 和 redo log 也是引擎层的 log 文件,undo log 提供了回滚和多个行版本控制(MVCC),在数据库修改操作时,不仅记录了 redo log,还记录了 undo log,如果因为某些原因导致事务执行失败回滚了,可以借助 undo log 进行回滚。

虽然 undo log 和 redo log 都是InnoDB 特有的,但 undo log 记录的是 逻辑日志,redo log 记录的是物理日志。undo log 日志用于存放数据被修改前的值,比如 update T set c=c+1 where ID=2; 这条 SQL,undo log 中记录的是 c 在 +1 前的值,如果这个 update 出现异常需要回滚,可以使用 undo log 实现回滚,保证事务一致性。

而多版本并发控制(MVCC) ,也用到了 undo log ,当读取的某一行被其他事务锁定时,它可以从 undo log 中获取该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。

undo log 是采用段 (segment)的方式来记录的,每个 undo 操作在记录的时候占用一个 undo log segment

rollback segment 称为回滚段,每个回滚段中有 1024 个 undo log segment,在以前的版本中,只支持一个 rollback segment,也就是只能记录 1024 个 undo log segment,MySQL 5.5 以后,可以支持 128 个 rollback segment,即支持 128*1024 个 undo 操作,还可以通过变量 innodb_undo_logs自定义 rollback segment 数量,默认是 128


总结

redo log 用来保证 crash-safe,binlog 用来保证可以将数据库状态恢复到任一时刻,undo log 是用来保证事务需要回滚时数据状态的回滚和 MVCC 时,记录各版本数据信息。

上一篇:图解mysql事务实现原理


下一篇:mysql-innodb的事务日志