MySQL(十九)MySQL事务日志(二)UndoLog
1 undo 日志概述
redo log
是事务持久性
的保障,而undo log
则是事务原子性
和一致性
的保证,如上图,在事务中更新数据的前置操作其实是需要将数据写入到 undo log
方便回滚。
事务需要保证原子性,也就是事务中的操作要么全部完成、要么全部不做。但有时候事务执行到一半也会出现一些情况:
- 情况一:事务执行过程中遇到一些情况,如服务器本身错误、操作系统错误甚至是断电
- 情况二:程序员自身进行了
rollback
操作
以上情况出现的时候,mysql就需要把数据恢复成事务之前的状态,这个过程称作回滚
,以满足事务原子性的特性。回滚操作
并不是直接将数据复原,而是:
Mysql为了回滚而记录的这些内容称之为撤销日志
或回滚日志
,由于查询不会修改任何用户记录,所以在查询操作执行时,不需要记录相应的undo log
此外,undo log
也会产生redo log
,即undo log
的产生会伴随着redo log
的产生,这是因为undo log
也需要进行持久化的保护。
2 undo log的作用
-
回滚数据:
undo log
是逻辑日志
,只是将数据库逻辑地恢复成事务执行之前的状态,所有的操作都被逻辑地取消了,但是数据结构
和页本身
在回滚之后可能大不相同。在多用户并发系统中,可能还有成百上千的并发事务在工作,数据库的工作就是协调这些事务对数据的并发访问,因此不能只是简单将数据库回滚到某一个事务执行之前的状态,这样会影响其他运行的事务。
-
MVCC:在
InnoDB存储引擎
中MVCC
的实现是通过undo log
来实现的,当用户读取一行记录的时候,该条记录已经被其他事务占用,那么当前事务可以通过undo log
读取之前的行版本信息,以实现非锁定读取。
3 undo log的数据结构
3.1 回滚段与undo 页
InnoDB
对undo log
的管理采用段的方式,也就是回滚段
(rollback segment),每个回滚段记录1024个undo log segment
,然后会在每个undo log segment
中进行undo页
的申请。
-
在
InnoDB
1.1之前只存在一个回滚段,因此只支持1024个在线并发事务,1.1之后支持128个回滚段,因此可以支持128 * 1024 个并发事务,可以通过innodb_undo_logs
查看支持的回滚段
个数:mysql> show variables like 'innodb_undo_logs' -> ; Empty set (0.01 sec)
undo log涉及的相关参数如下,一般情况下不会去动:
当开启一个事务需要写入undo log的时候,就需要去undo log segment中找一个空闲的位置,当有空位的时候就申请一个undo页,然后在这个申请到的undo页上进行undo log记录的写入。
但是为每一个事务都分配一个undo 页是非常浪费的,一个页的大小是16k,假设一个应用的TPS(每秒处理的事务数)是1000,那么1s就需要1000个页即16M,1分钟也就快1G了,除非mysql的清理速度特别快,否则很容易造成空间的浪费。
所以出现了对undo log页
的重用,即事务在commit
后并不会被立刻删除,而会被放到一个链表上,如果判断页面的使用小于3/4
则undo页面
可以被重用,其他事务的undo log记录
可以写在后面。因为undo log的记录
是离散的
,所以磁盘的清理效率不高。
3.2 回滚段与事务
- 每个
事务
只会使用一个回滚段
,而每个回滚段
可以同时服务于多个事务
- 当一个事务开始的时候就会指定一个回滚段,在事务进行的过程中,会将被修改数据的原始数据复制到回滚段
- 在回滚段中,事务会不断填充盘区,直到事务结束或者盘区空间用完。如果当前盘区不足,则会在段中申请下一个盘区,申请不到则会覆盖使用最初的盘区。
- 回滚段存在于undo表空间中,在数据库中存在多个undo表空间,但在某一个时刻只能使用一个undo表空间
当事务提交的时候,innodb
会进行下面的操作:
- 将
undo log
放入链表中,供后面的purge
操作(后台清理线程) - 判断
undo log
是否可以重用,可以则分配给下一个事务使用
3.3 回滚段中的数据分类
未提交的回滚数据
:数据对应的事务未提交,为了保证数据的读一致性,所以不能被其他事务覆盖数据已提交但是未过期的回滚数据
:该数据关联的事务已经提交,但是受到undo retention
参数保持时间的影响已提交并过期的回滚数据
:该数据关联的事务已经提交,并超过了undo retention
参数设置,当回滚段
满了之后,会被优先覆盖
如果要执行更新操作,会将原纪录放入undo log,并通过隐藏的回滚指针指向undo log中的原纪录。其他事务此时需要查询时,就是查询undo log中这行数据的最后一个历史版本。这也是事务提交之后不能马上删除undo log和undo log所在的页的原因,而是将这些undo log
页放到链表,由purge线程
判断是否清除
3.4 undo log的类型
-
insert undo log
即在insert操作中产生的
undo log
,因为是添加操作,所以只对当前记录可见,对其他事务不可见(事务隔离性的要求),所以可以直接删除,不需要purge
操作 -
update undo log
记录的是update 和 delete产生的undo log,
update undo log
由于还需要提供MVCC机制,因此不能提交完事务就删除,而是将这些undo log
页放到undo log链表,由purge线程
判断是否清除
4 undo log的生命周期
4.1 redo log + undo log简要生成过程
假设有两个数值A=1和B=2,将A修改为3B修改为4:
这里的redo log在事务提交前刷盘的,应该是由master线程将redo log buffer中的数据写入redo log file的。
- 1-7宕机,事务未提交不会对数据造成任何影响
- 8-9宕机,redo log完成了持久化但是数据没有,恢复之后可以选择使用
undo log
回滚或者继续执行事务 - 9之后宕机,为了满足持久性redo log在恢复之后会把数据刷回磁盘
4.2 详细生成过程
InnoDB行格式
有几个隐藏列:
执行insert操作的时候
比如这是添加一条记录,插入数据会生成一个undo log
,并且真实数据的回滚指针会指向这个undo log页中的相应记录
。undo log
会记录undo log的序号、插入主键的列和值,那么在进行rollback
的时候,直接通过主键的值将该条记录删除就可以了。
begin;
insert into user('name') values 'Tom';
执行update操作的时候
比如下面的更新语句,执行完之后会把老的列值
写入新的undo log
,并且回滚指针
指向该新的undo log
update user set name = 'Sun' where id = 1;
而对于更新主键的操作,会把数据的deletemark
标记打开,这时候并没有真正删除记录,真正的删除会交给清理线程去判断,然后会在后面插入一条新的数据,新的数据也会产生undo log,并且undo log的序号会递增。每次回滚的时候,只需要按照序号依次向前推
,就可以找到原始的数据了。
update user set id = 2 where id = 1;
undo log如何回滚的?
以上面的例子来说,执行rollback回滚,对应的流程应该是这样的:
- 通过undo no=3将主键id为2的记录删除
- 通过undo no=2把id=1的记录的
deletemark
还原为0 - 通过undo no=1把id=1的记录的name还原为tom
- 通过undo no=0把id=1的记录删除
undo log的删除
针对于insert undo log
,由于插入操作对其他事务不可见,因此可以直接删除;而对于update undo log
,由于要提供MVCC机制
,因此不能再提交事务的时候就进行删除,而是提交的时候放入undo log
链表,由purge线程
判断清除。
5 小结
undo是逻辑日志,对事务进行回滚,将数据库逻辑地恢复成事务执行之前的状态
redo log是物理日志,记录的是数据页的物理变化,redo log不是undo log的逆过程
标签:事务,log,记录,UndoLog,回滚,undo,MySQL,日志,redo From: https://www.cnblogs.com/tod4/p/17384663.html