前言
Undo log
是
InnoDB MVCC
事务特性的重要
组成部分
,同时可以提供
数据回滚
。当我们对记录做了
变更操作
时就会产生
undo记录
,Undo记录默认被记录到系统表空间(ibdata)中,但从
5.6
开始,也可以使用
独立的Undo 表空间
。
Undo记录中存储的是
老版本数据
,当一个
旧的事务
需要
读取数据
时,为了能读取到老版本的数据,需要
顺着undo链
找到满足其
可见性的记录
。当版本链很长时,通常可以认为这是个比较耗时的操作(例如bug#69812)。如果因为某些原因导致
事务失败或回滚
了,可以
借助
该
undo进行回滚
。
大多数对数据的
变更操作
包括
INSERT/DELETE/UPDATE
,其中
INSERT
操作在事务提交前只对当前事务可见,因此产生的Undo日志可以在
事务提交后直接删除
(谁会对刚插入的数据有可见性需求呢!!),而对于
UPDATE/DELETE
则需要
维护多版本
信息,在InnoDB里,UPDATE和DELETE操作产生的Undo日志被归成一类,即
update_undo
。
本文是对整个Undo生命周期过程的阐述,代码分析基于当前最新的MySQL5.7版本。
Undo Log
undo log和
redo log记录物理日志
不一样,它是
逻辑日志
。可以认为当
delete
一条记录时,undo log中会记录一条
对应的insert
记录,反之亦然,当
update
一条记录时,它记录一条
对应相反的update
记录。当执行rollback时,就可以从undo log中的逻辑记录
读取到相应
的内容并
进行回滚
。有时候应用到
行版本控制
的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录的
历史数据
是什么,让用户实现
非锁定一致性
读取。
为了保证事务并发操作时,在写各自的undo log时不产生冲突,InnoDB采用
回滚段
的方式来维护undo log的并发写入和持久化。回滚段实际上是一种 Undo
文件组织方式
,
每个回滚段
由
1024
个
undo log slot
组成。
每个undo操作
在记录的时候
占用一个undo log slot
。另外,
undo log也会产生redo log
,因为undo log也要
实现持久性
保护。
存储
MySQL5.5以后可以支持128个rollback segment,即支持128*1024个undo操作,还可以通过变量
innodb_undo_logs
(5.6版本以前该变量是 innodb_rollback_segments )
自定义
多少个
rollback segment
,
默认值为128
。undo log
默认存放
在
共享表空间
中。如果开启了
innodb_file_per_table
,将放在每个表的
.ibd
文件中。
在MySQL5.6中,undo的存放位置还可以通过变量
innodb_undo_directory
来
自定义存放目录
,默认值为"."表示datadir。
默认rollback segment全部写在一个文件中,但可以通过设置变量
innodb_undo_tablespaces
平均分配到多少个文件中。该变量
默认值为0
,即
全部写入一个表空间文件
。该变量为静态变量,只能在数据库示例停止状态下修改,如写入配置文件或启动时带上对应参数。但是innodb存储引擎在启动过程中提示,
不建议修改为非0
的值
delete/update操作的内部机制
当
事务提交
的时候,innodb
不会立即删除
undo log,因为
后续
还可能会用到undo log,如
隔离级别为repeatable read
时,
事务读取
的都是
开启事务时
的
最新提交行版本
,只要
该事务不结束
,
该行版本就不能删除
,即undo log不能删除。
但是在
事务提交
的时候,会将该事务对应的
undo log放入到删除列表
中,
未来通过purge来删除
。并且提交事务时,还
会判断undo log分配的页是否可以重用
,如果可以重用,则会分配给后面来的事务,
避免
为每个独立的事务
分配独立的undo log页
而
浪费存储空间和性能
。
通过undo log记录delete和update操作的结果发现:(insert操作无需分析,就是插入行而已)
-
操作实际上delete
,而是将delete对象不会直接删除
,标记为删除,打上delete flag
的删除操作是最终
线程完成的。purge
-
分为update
情况:update的列两种
。是否是主键列
- 如果
,在undo log中直接不是主键列
是如何反向记录
的。即update是直接进行的。update
- 如果
,update分两部执行:是主键列
该行,先删除
一行目标行。再插入
- 如果
参考undo数据结构:http://mysql.taobao.org/monthly/2015/04/01/
本文参考:https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html