文章目录
- 第一节、MySQL事务日志
- 1.1、redo日志
- 1.1.1、为什么需要REDO日志
- 1.1.2、REDO日志的好处、特点
- 1.1.3、redo的组成
- 1.1.4、redo的整体流程
- 1.2、Undo日志
- 1.2.1、如何理解Undo日志
- 1.2.2、Undo日志的作用
- 1.2.3、undo log的生命周期
第一节、MySQL事务日志
事务有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢?
- 事务的隔离性由锁机制实现。
- 而事务的原子性、一致性和持久性由事务的redo日志和undo日志来保证。
REDO LOG
称为 重做日志 ,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。
UNDO LOG
称为 回滚日志 ,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。
REDO和UNDO都可以视为是一种恢复操作,但是:
redo log
: 是存储引擎层(innodb)生成的日志,记录的是"物理级别"上的页修改操作,比如页号x、偏移量y写入了’zzz’数据。主要为了保证数据的可靠性;- undo log:是存储引擎层(innodb)生成的日志,记录的是逻辑操作日志,比如对某一行数据进行INSERT操作,那么undo log就记录一条与之相反的DELETE操作。主要用于事务的回滚(undo log记录的是每个修改操作的逆操作)和一致性非锁定读(undo log回滚行记录到某种特定的版本—MVCC,即多版本并发控制)。
1.1、redo日志
InnoDB存储引擎是以页为单位来管理存储空间的。在真正访问页面之前,需要把在磁盘上的页缓存到内存中的Buffer Pool之后才可以访问。所有的变更都必须先更新缓冲池中的数据,然后缓冲池中的脏页会以一定的频率被刷入磁盘( checkPoint机制
),通过缓冲池来优化CPU和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。
1.1.1、为什么需要REDO日志
一方面,缓冲池可以帮助我们消除CPU和磁盘之间的鸿沟,checkpoint机制可以保证数据的最终落盘,然而由于checkpoint 并不是每次变更的时候就触发的,而是master线程隔一段时间去处理的。所以最坏的情况就是事务提交后,刚写完缓冲池,数据库宕机了,那么这段数据就是丢失的,无法恢复。
另一方面,事务包含 持久性 的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失。那么如何保证这个持久性呢? 一个简单的做法 :在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘,但是这个简单粗暴的做法有些问题:
- 修改量与刷新磁盘工作量严重不成比例
有时候我们仅仅修改了某个页面中的一个字节,但是我们知道在InnoDB中是以页为单位来进行磁盘lo的,也就是说我们在该事务提交时不得不将一个完整的页面从内存中刷新到磁盘,我们又知道一个页面默认是16KB大小,只修改一个字节就要刷新16KB的数据到磁盘上显然是太小题大做了。 - 随机lo刷新较慢
一个事务可能包含很多语句,即使是一条语句也可能修改许多页面,假如该事务修改的这些页面可能并不相邻,这就意味着在将某个事务修改的Buffer Pool中的页面刷新到磁盘时,需要进行很多的随机IO,随机lo比顺序IO要慢,尤其对于传统的机械硬盘来说。
另一个解决的思路 :我们只是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以我们其实没有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把 修改 了哪些东西 记录一下 就好。比如,某个事务将系统表空间中 第10号
页面中偏移量为 100
处的那个字节的值 1
改成 2
。我们只需要记录一下:将第0号表空间的10号页面的偏移量为100处的值更新为 2 。
1.1.2、REDO日志的好处、特点
好处:
- redo日志降低了刷盘频率
- redo日志占用的空间非常小
特点:
- redo日志是顺序写入磁盘的
在执行事务的过程中,每执行一条语句,就可能产生若干条redo日志,这些日志是按照产生的顺序写入磁盘的,也就是使用顺序lo,效率比随机Io快。 - 事务执行过程中,redo log不断记录
redo log跟bin log的区别,redo log是存储引擎层产生的,而bin log是数据库层产生的。假设一个事务,对表做10万行的记录插入,在这个过程中,一直不断的往redo log顺序记录,而bin log不会记录,直到这个事务提交,才会一次写入到bin log文件中。
1.1.3、redo的组成
Redo log可以简单分为以下两个部分:
- 重做日志的缓冲 (
redo log buffer
) ,保存在内存中,是易失的。 - 重做日志文件 (
redo log file
) ,保存在硬盘中,是持久的。
1.1.4、redo的整体流程
- 先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝。
- 生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值。
- 当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式。
- 定期将内存中修改的数据刷新到磁盘中。
1.2、Undo日志
redo log是事务持久性的保证,undo log是事务原子性的保证。在事务中 更新数据 的 前置操作其实是要先写入一个 undo log
。
1.2.1、如何理解Undo日志
事务需要保证 原子性 ,也就是事务中的操作要么全部完成,要么什么也不做。但有时候事务执行到一半会出现一些情况,比如:
- 情况一:事务执行过程中可能遇到各种错误,比如服务器本身的错误 , 操作系统错误 ,甚至是突然断电导致的错误。
- 情况二:程序员可以在事务执行过程中手动输入
ROLLBACK
语句结束当前事务的执行。
以上情况出现,我们需要把数据改回原先的样子,这个过程称之为回滚,这样就可以造成一个假象:这个事务看起来什么都没做,所以符合原子性要求。
每当我们要对一条记录做改动时(这里的改动可以指INSERT
、DELETE
、UPDATE
),都需要"留一手"-把回滚时所需的东西记下来。比如:
- 你插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。(对于每个INSERT,InnoDB存储引擎会完成一个DELETE)
- 你删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。(对于每个DELETE,InnoDB存储引擎会执行一个INSERT)
- 你修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。(对于每个UPDATE,InnoDB存储引擎会执行一个相反的UPDATE,将修改前的行放回去)
MySQL把这些为了回滚而记录的这些内容称之为撤销日志或者回滚日志(即undo log
)。注意,由于查询操作( SELECT)并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的undo日志。此外,undo log 会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undolog也需要持久性的保护。
1.2.2、Undo日志的作用
- 回滚数据
用户对undo日志可能有误解:undo用于将数据库物理地恢复到执行语句或事务之前的样子。但事实并非如此。
undo是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,本身在回滚之后可能大不相同。
这是因为在多用户并发系统中,可能会有数十、数百甚至数千个并发事务。数据库的主要任务就是协调对数据记录的并发访问。比如,一个事务在修改当前一个页中某几条记录,同时还有别的事务在对同一个页中另几条记录进行修改。因此,不能将一个页回滚到事务开始的样子,因为这样会影响其他事务正在进行的工作。 - MVCC
undo的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是通过undo来完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。
1.2.3、undo log的生命周期
只有Buffer Pool的流程:
有了Redo Log和Undo Log之后: