InnoDB 中的 redo 和 undo log

讲到 InnoDB、MVCC 等概念时,我们时常听到 redo log 和 undo log 的名字,那么二者的作用是什么呢

Posted by ChenJY on April 13, 2019 | Viewed times

写在前面

讲到 InnoDBMVCC 等概念时,我们时常听到 redo logundo log 的名字,那么二者的作用是什么呢?其实二者并非事务操作独有,索引更新时也会记录 redo/undo log,甚至记录 undo log 时也会记录 redo log,而本文聚焦于事务方面的 redo/undo log

redo、undo log 的前世今生

假设现在只有 undo log 机制。

事务提交前为了保证突发宕机不丢更新,肯定要将 undo log 刷到磁盘,但这会造成多次磁盘 IO,但是考虑到日志文件是顺序写入,性能还好。事务提交后,需要将内存中的脏页立即同步到磁盘的数据文件中,这会造成至少一次磁盘随机 IO,影响性能。

因此数据库设计者就思考,如果事务提交后,脏页数据能在内存中缓存一段时间,而不需要立即被同步到磁盘文件,那么就能合并多次随机 IO 来提高性能。但是这样就会丧失事务的持久性,因为假如事务已经提交,数据却还在内存中时宕机了,重启后因为只有 undo log 无法恢复被提交的数据。

因此设计者们引入了另外一种机制来实现持久化,即 redo log。在事务提交前,只要将 redo log 持久化即可,不需要将数据持久化。当系统宕机时,虽然数据还没来得及刷回磁盘,但是 redo log 已经持久化了,系统可以根据 redo log 的内容,将所有数据恢复到最新的状态。

什么是 redo log

MySQL 中使用了大量内存 Cache 区域,对数据的修改操作会先直接修改内存中的 Page,但这些页不会立刻同步磁盘,这时内存中的数据已经和磁盘上的不一致了,我们称这种 Page 为脏页。试想一下这时候如果数据库宕机了,内存中这部分被修改的数据记录就丢失了,重启后也没办法恢复。

因此为了保证数据的安全性,在修改内存中的 Page 之后 InnoDB 会写 redo log,因为 redo log 是顺序写入的,而众所周知磁盘的顺序读写的速度远大于随机读写,因此这部分日志写操作对性能影响较小。然后,InnoDB 会在事务提交前将 redo log 保存到磁盘中。这里所说的 redo log 是物理日志而非逻辑日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。

当数据库意外重启时,会根据 redo log 进行数据恢复,如果 redo log 中有事务提交,则进行事务提交修改数据。

什么是 undo log

redo log 不同,undo log 一般是逻辑日志,根据每行记录进行记录。例如当 DELETE 一条记录时,undo log 中会记录一条对应的 INSERT 记录,反之亦然当 UPDTAE 一条记录时,它记录一条对应反向 UPDATE 记录。

分布式事务 Seata 的 GTS 模式就是这么实现的,只不过这个过程放在了客户端代码里,而且支持的 SQL 类型有要求

当数据被修改时除了会记录 redo log 还会记录 undo log,通过 undo log 一方面可以实现事务回滚,另一方面可以根据 undo log 回溯到某个特定的版本的数据,实现 MVCC 的功能。

只靠 undo log 行么?

只有 undo log 的情况下就必须保证事务提交前,脏页必须刷回磁盘,否则宕机时这部分数据修改就在内存中丢失了,破坏了持久性,但是如上文提到的,这种办法性能太差,必须改进。

只靠 redo log 行么?

只有 redo log 的情况下就不能在事务提交前刷脏,我们假设有个大事务更新数据量很大,更新完一部分必须刷一部分脏页回磁盘然后再 load 其他的数据进内存操作,此时如果更新到一半宕机了,那么数据库里存在一部分新数据一部分旧数据,而事务又没有提交,因此应该回滚,只有 redo log 的情况下无法完成。

redo/undo log 的持久化

redo log 由两部分组成,一部分是内存中的 redo log buffer,这部分是易失的,重启就没了;二是磁盘上的 redo log file,是持久化的。

InnoDB 通过 force log at commit 技术来实现事务的持久化特性。为了保证每次 redo log 都能写入磁盘上的日志文件中,每次将内存中的 redo log buffer 内容同步磁盘时都会调用一次 fsync

OS 中 write 和 fsync 是不同的操作,我们以为调用了 write 就万事大吉,数据一定到磁盘了,其实不一定,通常情况下 write 只是到了磁盘 IO 缓冲区,何时 fsync 由 OS 控制,这里通过程序强制调用来保证日志一定刷到磁盘

参考资料


这是一个不定时更新的、披着程序员外衣的文青小号。

在这里,既分享极客技术,也记录人间烟火,欢迎关注。

0

Comment