MySQL事务中的 Redo 与 Undo 日志
好了,事务相关最后一个知识点,就是剩下的 Redo 和 Undo 日志相关的内容了。在学习它们之间,我们要先复习一下事务的四大特性,小伙伴们还记得是哪四大特性吗?
ACID,原子性、一致性、隔离性、持久性。这个在之前的文章 MySQL事务特性与自动提交https://mp.weixin.qq.com/s/SnLqdIPl2aMYIDjXX8uHvg 中就有学习过,不记得的小伙伴可以回去复习一下。
我们之前讲的 锁 和 MVCC 主要解决的是隔离性问题,虽说我可能会顺嘴说到什么加了锁就可以让其它事务读取的结果保持一致之类的话,但是,这个一致只是说在两个不同事务之间相互读取的数据与我们主观感受的一致,不是真正的事务四大特性中的那个一致性。它们两个真正的作用是让不同的事务之间不要产生干扰,从而实现隔离性。
那么剩下的三大特性是由什么来实现或者说来约束的呢?那就是我们今天要学习的这两种日志。
Redo Log
Redo 重做的意思,这个英文名起的太直观了吧。它主要是提供写入操作,恢复提交事务修改的页操作,物理级别,主要就是保证事务的持久性。
什么意思呢,我们直接从它的作用说起。
一是 Redo Log 可以起到性能优化的作用。数据库为了提升性能,并不是每次读写都是直接走硬盘的,而是还有一层缓存,这个缓存通常也是存储在内存中。内存中的数据有什么特点?速度快,但不持久,然而,Redo Log 就可以做为内存与磁盘的中间介质,当需要将数据从内存刷盘到硬盘上时,可以起到降低刷盘频率,提升性能的作用。
另一点也是非常重要的一点,当我们修改数据后,事务刚刚提交,突然服务实例挂了,或者整个服务器挂了,内存缓冲中的数据(脏页)还没有刷到磁盘的话,不就丢失了?不怕,Redo Log 在这里就起到非常重要的作用了,它可以保证服务实例在重启后继续完成事务,其实也就是按照日志中记录的内容再执行一遍之前未刷盘的事务。
现在知道为什么说 Redo Log 是保证持久性的了吧,最终,它都会让数据落盘。我们认为,数据落盘,保存到真正的磁盘之上,就可以说数据是持久的了。至于数据盘损坏或者其它情况,则不是数据库系统所关心的了。
因此,整个数据落盘的过程应该是这样的。
- 原始数据进内存缓冲区,修改后进行内存拷贝形成新数据
- 生成一条 Redo Log 写入日志内存缓冲(和数据缓冲不在一起),记录数据被修改后的值
- 事务提交时,先将日志缓冲中的内容刷到重做日志文件中,采用追加写方式
- 之后才会根据系统设置定期将内存中修改的真实数据刷新到磁盘
在这个过程中,MySQL 使用的是一种叫做,WAL 的技术,Write-Ahead Logging,说人话就是不管干嘛,都要先写日志,再刷磁盘。在持久化一个数据页之前,一定是先将内存中相应的日志页持久化,然后才把真实数据持久化。它是顺序写入日志,比随机 IO 好,性能更高,速度也非常快,所以这里虽然也是写磁盘,但性能是可以接受的。同时我们要注意的一点是,Redo 日志是不断写入的,即使事务没提交,它也会写入。而大家非常熟悉的 Binlog 只有事务提交之后才一次性写完整个事务的日志。
从上面的过程可以看出,Redo Log 也是由两方面组成的,分别是:
- 重做日志的缓冲 redo log buffer,是一块连续的内存空间,我们可以配置日志的缓冲空间 innodb_log_buffer_size 这个变量,它的默认值是 16M 。
- 重做日志文件 redo log file,真实在磁盘上的日志记录文件,大家可以在你的数据库数据目录中看到 ib_logfile0、ib_logfile1 这两个文件,也有可能会有更多,生成的文件数量也是可配置的,可以自己找一下相关的配置参数。
最后我们再来看下 Redo Log 的刷盘策略。它是根据 innodb_flush_log_at_trx_commit 这个参数来配置的,有三种值可以进行配置。
- 0 表示事务提交时不刷盘,默认主线程间隔1s进行一次重做日志的刷盘,性能好,1s 这个时间中间的事务如果发生意外有可能会出现问题
- 1 表示每次事务提交时进行刷盘(默认值),性能差,但最安全,因为事务只要一提交马上就保存到日志文件中。这个选项是完整保持 ACID 中的 D 特性的,因为其他两个都会有至少 1s 的延迟。
- 2 表示每次事务提交时只把缓冲区内容写入操作系统的文件系统缓存(Page Cache),数据库不刷盘,而是由操作系统决定,性能中间,但如果操作系统宕机了,也会有问题。
Undo Log
一个 Redo ,这又来个 Undo ,难道是俩兄弟?它们是互逆操作?不不不,千万别这么想。兄弟有可能,毕竟都是日志操作,但并不是互逆的。
根据名字来看,Undo 是不做的意思,更确切的说是回滚操作的意思,它其实是针对写操作的逆操作。比如 Insert/Update/Delete 这些操作,会记录操作的数据信息形成我们上篇文章中学习过的 版本链 ,用于实现 快照读 以及实现数据的逆向回滚。它实际执行的是逆操作,比如执行一个 INSERT ,出现问题后使用 undo 会执行一个 DELETE 操作。
是的,你猜到了,rollback 就是重放一下 Undo 日志。当数据库崩溃时,也会通过 Undo 还原之前有问题的数据。根据这两点保证数据的原子性和一致性(要么完成,要么全部失败,最终一致)。
Undo Log 主要的配置参数就是一个 innodb_undo_logs 参数,用于进行回滚段的设置,关于回滚段的概念大家可以自行查阅下相关的资料,包括其它的一些配置也可以自己查阅一下哦。
最后,在硬盘上,同样也是在我们的数据库数据文件目录中,undo_001、undo_002这些命名的文件就是我们的 Undo Log 落盘日志文件。它也会同时产生 redo log ,也就是说 undo log 也是需要 redo log 来进行持久性保护的。
总结
今天的内容很理论,但也很简单,并不是很复杂的东西,就是有两种日志,分别用于解决一些问题,同时我们的数据库要保证先写日志再写真实数据。好了,事务相关的学习先告一段落,核心的 MySQL 理论知识也告一段落。
参考资料:
《MySQL是怎样运行的》
尚硅谷https://www.bilibili.com/video/BV1iq4y1u7vj?p=169