MySQL 有两大重要日志模块,redo log(重做日志)和 binlog(归档日志)。
重要日志模块: redo log
redo log 可以理解为一个日志文件,当 MySQL 接收到很多更新请求时,一时处理不完(因为数据库储存数据量大,查找更新慢),就可以把请求先写入 redo log,然后再慢慢写入数据库。其实就是 MySQL 里经常说到的 WAL 技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘。
InnoDB 的 redo log 是固定大小的,所以就会出现写满的情况。比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。
write pos 是记录当前位置,一边写一边移动,写到第 3 号文件末尾后,就回到 0 号文件开头。而 checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据库。
write pos 和 checkpoint 之间是还空着的部分,可以用来记录新的操作,如果 write pos 追上 checkpoint,表示 redo log 满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
重要的日志模块:binlog
上面我们聊到的 redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。
这两种日志有以下三点不同:
- redo log 是 InnoDB 引擎独有的,binlog 是 Server 层实现的,所有引擎都可以用。
- redo log 是物理日志,记录的是“在某个数据页上做了什么修改”; binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
- redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
- redo 在 事务执行过程中 会不断的写入,而 binlog 是在 事务最终提交前 写入的。
binlog 记录的都是事务操作内容,binlog 有三种模式:Statement(基于 SQL 语句的复制)、Row(基于行的复制) 以及 Mixed(混合模式)。具体这三种模式的区别请看主从同步和主备同步专栏。
两阶段提交
- 为什么必须有“两阶段提交”呢?这是为了让两份日志之间的逻辑一致。redolog 的写入分为了两个步骤: prepare 和 commit。
如果不使用两阶段提交:
先写入 redo 再写入 binlog,那么当写入 redo 后,数据库发生异常中断。重启后,redo log 写完后,仍能够把数据恢复回来。但是 binlog 就没有记录这条语句,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,会导致数据不一致。
如果先写 binlog 再写入 redo,那么在 binlog 写入后进行 crash,由于 redo 还没来的及写,崩溃恢复(崩溃恢复用 redo)以后这个事务无效, 单用 binglog 来恢复时就多了一个事务出来。
具体而言,崩溃恢复在两阶段提交不同时刻的判断规则如下:图中浅绿色在 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)的情况,崩溃恢复过程中事务还是会被提交。
总结:
redo log 记录的,即使异常重启,都会刷新到磁盘,而 bin log 记录的, 则主要用于备份。
binlog 写完以后 MySQL 发生崩溃,这时候 binlog 已经写入了,之后就会被从库(或者用这个 binlog 恢复出来的库)使用。 所以,在主库上也要提交这个事务。采用这个策略,主库和备库的数据就保证了一致性。
声明:此篇文章的主体内容参考自极客时间的 MySQL 实战 45 讲,非本人原创,只是在学习的过程中觉得有必要将其写入个人博客之中,且最终目的只是为了方便自己或有需要的人进行查阅。此外,若需转载本文仍需本人同意。
上一篇:MySQL 学习笔记-----基础架构
下一篇: MySQL 学习笔记-----事务隔离
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于