undo log:世上真有后悔药
本文内容
1. 为什么需要 undo log?
InnoDB 存储引擎是支持事务的,而事务有一个非常重要的特征是 原子性,即事务中的所有操作,要么全部完成,要么全部失败。
但是,在实际运行过程中,就会有出现某个 事务执行到一半 的情况,例如:
事务执行过程中 出现错误,如实例崩溃、突然断电宕机等;
开发人员在事务执行过程中手动执行 ROLLBACK 语句结束当前事务。
还有 两阶段提交时,在某时刻崩溃后,如果 binlog 还未刷盘,则要回滚这个事务。
这些情况都会导致 事务执行到一半时结束,但是在执行过程中可能已经修改很多记录了,那为了 保证原子性,需要 将数据变回原来的样子,就像这个事务没有执行过一样,这个过程就称为 回滚(rollback)。
那怎么才能将数据变回原来的样子呢?根据什么进行回滚呢?这就是 undo log 需要做的事了。
2. 什么是 undo log?
由于要将数据变回原来的样子,所以我们肯定得 先把原来的数据记录下来。
当然,并不是执行所有 SQL 语句时都要记录原来的数据,只用记录对数据有修改的就行了,也就是 INSERT、UPDATE、DELETE,比如:
- 插入 一条记录时,至少把这条记录的 主键值 记录下来,这样回滚时只需要把这条记录删掉即可;
- 删除 一条记录时,要把这条 记录的所有内容 都记录下来,这样回滚时再把这条记录插入即可;
- 更新 一条记录时,至少要把 被更新的列的旧值 记录下来,这样在回滚时只需要把这些列更新回旧值即可。
这些提前需要记录的内容放在哪儿呢?想必你也已经想到了,就是 undo log。
其实不同的操作(增删改)对应的 undo log 格式是不一样的,这里就不细说了。
事务的 原子性就是依靠 undo log 保证的,在事务没提交前,会先记录更新前的数据到 undo log 中,当需要进行回滚时,就可以利用该日志进行回滚了。
3. undo log 何时刷盘?
我们知道,MySQL 的数据页有很多种类型,undo log 也有自己的数据页,叫做 undo 页。
在 了解 Buffer Pool 中说过,undo 页也是存储在 Buffer Pool 中的,所以在 undo log 写入 undo 页时,其实也是 在 Buffer Pool 中写,所以 undo 页和其他数据页一样,在刷脏页时就会进行持久化。
在 Buffer Pool 的文章中也说过,脏页的持久化是由 redo log 保证的,所以 undo 页的修改也会记录到 redo log 中,我们并不用担心崩溃后还未刷盘的 undo log 会丢失。
4. 总结
由于在某些情况下,事务可能会在执行过程中结束,例如事务在执行过程中 出现错误、执行 ROLLBACK、或者 在崩溃恢复时需要回滚。
但是在事务结束之前可能已经修改过很多数据了,这时候就需要 将数据变回原来的样子,所以就需要 记录修改之前的记录内容,这就是 redo log 做的事。
undo log 保证了事务的原子性,让事务中的操作要么全部完成,要么全部失败。
此外,undo log 对应的数据页类型是 undo 页,也会存储在 Buffer Pool 中,对 undo 也的修改也会记录在 redo log 中,从而保证了 持久性。
顺便说一下,undo log 除了保证事务原子性之外,还有一个重要的作用是 实现 MVCC 的重要条件之一,在事务讲解中会详细介绍。
5. 参考文章
- 《MySQL 是怎样运行的》