在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,预知后事如何,待听下回分解
有个问题,如果异常崩溃,如果之前有事务没提交,起来以后回滚会在实例可连通后继续进行,只不过这个表上的事务会有影响.这个情况下,能通过这个功能知道有多少待回滚的undo,来估算回滚时间么?