文章目录
1. redo日志
InnoDB存储引擎是以页
为单位来管理存储空间的。在真正访问页面之前,需要把在磁盘上
的页缓存到内存中的
Buffer Pool之后才可以访问。所有的变更都必须先更新缓冲池
中的数据,然后缓冲池中的脏页
会以一定的频
率被刷入磁盘(checkPoint机制),通过缓冲池来优化CPU和磁盘之间的鸿沟,这样就可以保证整体的性能不
会下降太快。
1.1 为什么需要REDO日志
一方面,缓冲池可以帮助我们消除CPU和磁盘之间的鸿沟,checkpoint机制可以保证数据的最终落盘,然而由于
checkpoint并不是每次变更的时候就触发
的,而是master线程隔一段时间去处理的。所以最坏的情况就是事务提交
后,刚写完缓冲池,数据库宕机了,那么这段数据就是丢失的,无法恢复。
另一方面,事务包含持久性的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个
事务对数据库中所做的更改也不能丢失。
那么如何保证这个持久性呢?一个简单的做法:在事务提交完成之前把该事务所修改的所有页面都刷新到磁盘,
但是这个简单粗暴的做法有些问题:
- 修改量与刷新磁盘工作量严重不成比例
- 随机I0刷新较慢
另一个解决的思路:我们只是想让已经提交了的事务对数据库中数据所做的修改永久生效,即使后来系统崩溃,在重启后也能把这种修改恢复出来。所以我们其实没有必要在每次事务提交时就把该事务在内存中修改过的全部页面刷新到磁盘,只需要把修改
了哪些东西记录一下
就好。比如,某个事务将系统表空间中第10号页面中偏移
量为100处的那个字节的值1改成2。我们只需要记录一下:将第0号表空间的10号页面的偏移量为100处的值
更新为2。
InnoDB引擎的事务采用了WAL
技术(Write-Ahead Logging),这种技术的思想就是先写日志,再写磁盘,只
有日志写入成功,才算事务提交成功,这里的日志就是redo log。当发生宕机且数据未刷到磁盘的时候,可以通
过redo log来恢复,保证ACID中的D,这就是redo log的作用。
1.2 redo日志的好处、特点
1.2.1 好处
- 降低了刷盘频率
- 占用空间比较小
1.2.2 特点
- 顺序写入磁盘
- 事务执行过程中,redo log不断记录
1.3 redo日志的组成
- 重做日志的缓冲(reldolog buffer),保存在内存中,是易失的。
- 重做日志文件(redo log file),保存在硬盘中,是持久的。
1.4 redo的整体流程
以一个更新事务为例,redo log流转过程,若下图所示:
1.5 redo log的刷盘策略
redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以一定的频率
刷入到真正的redo log file中。这里的一定频率怎么看待呢?这就是我们要说的刷盘策略。
注意,redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到文件系统缓存
(page
cache)中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给系统自己来决定(比
如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那
么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。
针对这种情况,InnoDB给出innodb_flush_log_at_trx_commit
参数,该参数控制commit提交事务时,如何将redo log buffer中的日志刷新到redo log file中。它支持三种策略:
-
设置为0
:表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次重做日志的同步)
-
设置为1
:表示每次事务提交时都将进行同步,刷盘操作(默认值
)
-
设置为2
:表示每次事务提交时都只把redo log buffer内容写入page cache,不进行同步。由os自己决定什么
时候同步到磁盘文件。
2. Undo日志
redo log是事务持久性的保证,undo log是事6原子性的保证。在事务中更新数据
的前置操作
其实是要先写入一
个undo log
。
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的产生,这是因为undo log也需要持久
性的保护。
2.2 Undo日志的作用
- 回滚数据
- MVCC
2.2 undo的类型
- insert undo log
insert undo log是指在insert操作中产生的undo log。因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该undo log可以在事务提交后直接删除。不需要进行purge操作。 - update undo log
update undo log记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。
2.3 undo log的生命周期
2.3.1 简要生成过程
2.3.2 详细生成过程
对于InnoDB引擎来说,每个行记录除了记录本身的数据之外,还有几个隐藏的列:
- DB_ROW_ID:如果没有为表显式的定义主键,并且表中也没有定义唯一索引,那么InnoDB会自动为表添加一个row_id的隐藏列作为主键。
- DB_TRX_ID:每个事务都会分配一个事务ID,当对某条记录发生变更时,就会将这个事务的事务ID写入trx_id中。
- DB_ROLL_PTR:回滚指针,本质上就是指向undo log的指针。
当我们执行insert时:
begin;
insert into user(name) values ('tom');
插入的数据都会生成一条insert undo log,并且数据的回滚指针会指向它。undo log会记录undo log的序号、插入
主键的列和值.…,那么在进行rollback的时候,通过主键直接把对应的数据删除即可。
当我们执行update时:
对于更新的操作会产生update undo log,并且会分更新主键的和不更新主键的,假设现在执行:
update user set name='sun' where id = 1;
当更新主键时
update user set id=2 where id = 1;
对于更新主键的操作,会先把原来的数据deletemark标识打开,这时并没有真正的删除数据,真正的删除会交给
清理线程去判断,然后在后面插入一条新的数据,新的数据也会产生undo log,并且undo log的序号会递增。
可以发现每次对数据的变更都会产生一个undo log,当一条记录被变更多次时,那么就会产生多条undo log,
undo log记录的是变更前的日志,并且每个undo log的序号是递增的,那么当要回滚的时候,按照序号依次向前 推
,就可以找到我们的原始数据了。
2.3.3 undo log是如何回滚的
以上面的例子来说,假设执行rollback,那么对应的流程应该是这样:
- 通过undo no=3的日志把id=2的数据删除
- 通过undo no=2的日志把id=1的数据的deletemark还原成0
- 通过undo no=1的日志把id=1的数据的name还原成Tom
- 通过undo no=o的日志把id=1的数据删除
2.3.4 undo log的删除
- 针对于insert undo log
- 针对于update undo log
2.4 总结
undo log是逻辑日志,对事务回滚时,只是将数据库逻辑地恢复到原来的样子。
redo log是物理日志,记录的是数据页的物理变化,undo log不是redo log的逆过程。