undo log 的作用
事务需要保证 原子性
,也就是事务中的操作要么全部完成,要么什么也不做。但有时候事务执行到一半会出现一些情况,比如:
情况一:事务执行过程中可能遇到各种错误,比如
服务器本身的错误
,操作系统错误
,甚至是突然断电
导致的错误。情况二:程序员可以在事务执行过程中手动输入
ROLLBACK
语句结束当前事务的执行。
以上情况出现,我们需要把数据改回原先的样子,这个过程称之为 回滚
,这样就可以造成一个假象:这个事务看起来什么都没做,所以符合 原子性
要求。
总而言之,undo log记录的就是操作的逆操作,插入一条记录,undo 记录的就是delete操作
Undo日志的作用
作用1:回滚数据
undo是逻辑日志,只是将数据库逻辑恢复到原来的样子,但对具体的数据结构和页是不会坐修改的,因为其它事务也可能会在相同的页进行数据操作,如果在数据结构和页层面进行回滚,会影响到其它事务正在进行的操作。
-
作用2:MVCC
undo的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是通过undo来完成。当用户读取一行记录时,若该记录以及被其他事务占用,当前事务可以通过undo读取之前的行版本信息,以此实现非锁定读取。
undo的存储结构
回滚段与undo页
InnoDB对undo log的管理采用段的方式,也就是 回滚段(rollback segment)
。每个回滚段记录了 1024
个 undo log segment
,而在每个undo log segment段中进行 undo页
的申请。
在
InnoDB1.1版本之前
(不包括1.1版本),只有一个rollback segment,因此支持同时在线的事务限制为1024
。虽然对绝大多数的应用来说都已经够用。从1.1版本开始InnoDB支持最大
128个rollback segment
,故其支持同时在线的事务限制提高到 了128*1024
。
mysql> show variables like 'innodb_undo_logs';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| innodb_undo_logs | 128 |
+------------------+-------+
undo的页是可以重用,并不会为每一个事务都单独分配一个页,当事务提交时,并不会立刻删除undo 页,因为这个页可能夹杂着其它事务的undo log ,undo log 在事务commit之后,会被放到一个链表中,然后判断该页的使用空间,如果小于4分之3的话,就不会被回收,其它事务的undo log 可以记录到当前undo 页的后面。
回滚段与事务
每个事务只会使用一个回滚段,一个回滚段在同一时刻可能会服务于多个事务。
当一个事务开始的时候,会制定一个回滚段,在事务进行的过程中,当数据被修改时,原始的数据会被复制到回滚段。
在回滚段中,事务会不断填充盘区,直到事务结束或所有的空间被用完。如果当前的盘区不够 用,事务会在段中请求扩展下一个盘区,如果所有已分配的盘区都被用完,事务会覆盖最初的盘 区或者在回滚段允许的情况下扩展新的盘区来使用。
回滚段存在于undo表空间中,在数据库中可以存在多个undo表空间,但同一时刻只能使用一个 undo表空间。
当事务提交时,InnoDB存储引擎会做以下两件事情:
将undo log放入列表中,以供之后的purge操作
判断undo log所在的页是否可以重用,若可以分配给下个事务使用
回滚段中的数据分类
事务提交后不能马上删除undo log及undo log所在的页。这是因为可能还有其他事务需要通过undo log来得到行记录之前的版本。故事务提交时将undo log放入一个链表中(MVCC版本链),是否可以最终删除undo log以undo log所在页由purge线程来判断。
未提交的回滚数据(uncommitted undo information)
:该数据所关联的事务并未提交,用于实现读一致性,所以该数据不能被其他事务的数据覆盖。
已经提交但未过期的回滚数据(committed undo information)
:该数据关联的事务已经提交,但是仍受到undo retention参数的保持时间的影响。
事务已经提交并过期的数据(expired undo information)
:事务已经提交,而且数据保存时间已经超过 undo retention参数指定的时间,属于已经过期的数据。当回滚段满了之后,就优先覆盖“事务已经提交并过期的数据"。
undo的类型
在InnoDB存储引擎中,undo log分为:
-
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线程进行最后的删除。
undo log的生命周期
简要生成过程
以下是undo+redo事务的简化过程
假设有两个数值,分别为A=1和B=2,然后将A修改为3,B修改为4
只有Buffer Pool的流程:
修改数据和查询数据,都是不存在就从磁盘中加载进buffer pool,更新数据时就先更新buffer pool中的数据,然后定时刷入磁盘中
![]()
有了Redo Log和Undo Log之后:
在更新Buffer Pool中的数据之前,我们需要先将该数据事务开始之前的状态写入Undo Log中。假设更新到一半出错了,我们就可以通过Undo Log来回滚到事务开始前。并将修改的数据同步到redo 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;
这时会把老的记录写入新的undo log,让回滚指针指向新的undo log,它的undo no是1,并且新的undo log会指向老的undo log(undo no=0)。
UPDATE user SET id=2 WHERE id=1;
对于更新主键的操作,会先把原来的数据deletemark标识打开,这时并没有真正的删除数据,真正的删除会交给清理线程去判断,然后在后面插入一条新的数据,新的数据也会产生undo log,并且undo log的序号会递增。
可以发现每次对数据的变更都会产生一个undo log,当一条记录被变更多次时,那么就会产生多条undo log,undo log记录的是变更前的日志,并且每个undo log的序号是递增的,那么当要回滚的时候,按照序号
依次向前推
,就可以找到我们的原始数据了。
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
因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作。
针对于update undo log
该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。
小结
undo log是逻辑日志,对事务回滚时,只是将数据库逻辑地恢复到原来的样子。
redo log是物理日志,记录的是数据页的物理变化,undo log不是redo log的逆过程。