在InnoDB存储引擎中,undo log用来完成事务的回滚以及MVCC的功能。但是InnoDB本身提供对于undo log的分析或者查询功能非常有限。用户仅能通过SHOW ENGINE INNODB STATUS中的History list length信息的值当前undo log在history列表中的数量。网上已经有些文章分析了undo log的存储结构,但很少对InnoDB的undo log进行解析。本博文将详细介绍InnoDB存储引擎中的undo log格式,使读者能更为深入了解其实现。 在InnoDB存储引擎中,undo log可分为以下两种类型:
- insert undo log
- update undo log
insert undo log是指在insert操作中产生的undo log。因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该undo log可以在事务提交后直接删除。不需要进行purge操作。insert undo log的格式如左图所示(点击可看原图)。
图中*表示对存储的字段进行了压缩。insert undo log开始的前两个字节next记录的是下一个undo log的位置,通过该next的字节可以知道一个undo log所站的空间字节数。类似地,尾部的两个字节记录的是undo log的开始位置。type_cmpl占用一个字节,记录的是undo地类型,对于insert undo log,该值总是为11。undo_no记录事务的ID,table_id记录undo log所对应的表对象。这两个值都是用于进行压缩的。接着的部分记录了所有主键的列和值。在进行rollback操作时,根据这些值可以定位到具体的记录,然后进行删除即可。
update undo log记录的是对delete和update操作产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。update undo log的结构如右图所示(点击可看原图)。
update undo log相对于之前介绍的insert undo log,记录的内容更多,所需占用的空间也更大。next、start、undo_no、table_id与之前介绍的insert undo log部分相同。这里的type_cmpl,由于update undo log本身还有分类,故其可能的值如下:
- 12 (TRX_UNDO_UPD_EXIST_REC)
- 13 (TRX_UNDO_UPD_DEL_REC)
- 14 (TRX_UNDO_DEL_MARK_REC)
undo log可以分为上述3种类型,其分别代表了更新non-delete-mark的记录,将delete的记录标记为not delete,将记录标记为delete。
紧接着的部分记录update_vector信息,update_vector表示update操作导致发生改变的列。每个修改的列信息都要记录的undo log中。对于不同的undo log类型,可能还需要记录对索引列所做的修改。
Oracle和Microsoft SQL Server数据库都由内部的数据字典来观察当前undo的信息,InnoDB存储引擎在这方面做得还是不够,DBA只能通过原理和经验来进行判断。InnoSQL对information_schema进行了扩展,添加了两张数据字典表,这样用户可以非常方便和快捷地查看undo的信息。
首先增加的数据字典表为INNODB_TRX_ROLLBACK_SEGMENT。顾名思义,这个数据字典表用来查看rollback segment(回滚段)。InnoDB 1.1版本开始,其一共有128个rollback segment,每个rollback segment支持1024个undo log段,因此一共支持128*1024个同时并发在线事务。而之前的版本仅有1个回滚段,故最大支持并发事务为1024。
例如,用户可以通过下面的命令来查看rollback segment所在的页(InnoDB 1.2版本之前,rollback segment都放在共享表空间内,故space值都为0。InnoDB 1.2版本开始支持独立的rollback segment表空间):
mysql> SELECT segment_id,space,page_no FROM INNODB_TRX_ROLLBACK_SEGMENT; +------------+-------+---------+ | segment_id | space | page_no | +------------+-------+---------+ | 0 | 0 | 6 | | 1 | 0 | 45 | | 2 | 0 | 46 | ...... 128 rows in set (0.00 sec)
另一张数据字典表为INNODB_TRX_UNDO,用来记录事务对应的undo log,方便DBA和开发人员详细了解每个事务产生的undo量。下面将演示如和使用INNODB_TRX_UNDO表,首先根据如下代码创建测试表t。
CREATE TABLE t ( a INT, b VARCHAR(32), PRIMARY KEY(a), KEY(b) )ENGINE=InnoDB;
接着插入一条记录,并尝试通过INNODB_TRX_UNDO观察该事务的undo log的情况:
mysql> BEGIN; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO t SELECT 1,’1’; Query OK, 1 row affected (0.00 sec) Records: 1 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM information_schema.INNODB_TRX_UNDO\G; *************************** 1. row *************************** trx_id: 3001 rseg_id: 2 undo_rec_no: 0 undo_rec_type: TRX_UNDO_INSERT_REC size: 12 space: 0 page_no: 334 offset: 272 1 row in set (0.00 sec)
通过数据字典表可以看到,事务ID为3001,rollback segment的ID为2,因为是该条事务的第一个操作,故undo_rec_no为0。之后可以看到插入的类型为TRX_UNDO_INSERT_REC,表示是insert undo log。size表示undo log的大小,占用12字节。最后的space、page_no、offset表示undo log开始的位置。打开文件ibdata1,定位到页(334,272),并读取12字节,可得到如下内容:
01 1c 0b 00 16 04 80 00 00 01 01 10
上述就是undo log实际的内容,之前对于insert undo log格式的介绍,可以整理得到:
01 1c # 下一个undo log的位置 272+12=0x011c 0b # undo log的类型,TRX_UNDO_INSERT_REC为11 00 # undo log的记录,等同于undo_rec_no 16 # 表的ID 04 # 主键的长度 80 00 00 01 # 主键的内容 01 10 # undo log开始的偏移量,272=0x0110
此外,由于知道该undo log所在的rollback segment的ID为2,用户还可以通过数据字典表INNODB_TRX_ROLLBACK_SEGMENT来查看当前rollback segment的信息,如:
mysql> SELECT segment_id,insert_undo_list,insert_undo_cached -> FROM information_schema.INNODB_TRX_ROLLBACK_SEGMENT -> WHERE segment_id=2\G; *************************** 1. row *************************** segment_id: 2 insert_undo_list: 1 insert_undo_cached: 0 1 row in set (0.00 sec)
可以看到insert_undo_list为1。若这时进行事务的COMMIT操作,再查看该数据字典表:
mysql> COMMIT; Query OK, 0 rows affected (0.00 sec) mysql> SELECT segment_id,insert_undo_list,insert_undo_cached -> FROM information_schema.INNODB_TRX_ROLLBACK_SEGMENT -> WHERE segment_id=2\G; *************************** 1. row *************************** segment_id: 2 insert_undo_list: 0 insert_undo_cached: 1 1 row in set (0.00 sec)
可以发现,insert_undo_list变为0,而insert_undo_cached增加为1。这就是undo页重用,即下次再有事务需要向该rollback segment申请undo页时,可以直接使用该页。
update undo log较之insert undo log要复杂的多,因此将在第二篇中进行分析。so,预知后事如何,待听下回分解