在
上一篇中已经介绍了InnoDB undo log的组织结构,并通过一个示例并结合InnoSQL来分析insert undo log记录格式。本篇中介绍update undo log的记录格式。update undo log有以下三种类型:
这里undo log的类型为1c而不是0c这是因为这部分还保存了其他信息,这里1表示更新操作没有更新其他索引列。若更新辅助索引列b,如:
类型 | 十六进制值 | 说明 |
TRX_UNDO_UPD_EXIST_REC | 0x0c | 更新一个not delete mark的记录 |
TRX_UNDO_UPD_DEL_REC | 0x0d | 更新一个delete mark记录 |
TRX_UNDO_DEL_MARK_REC | 0x0e | 将记录标记为delete mark |
接着来看一个具体的例子,首先根据如下清单创建测试表t并导入测试数据:
CREATE TABLE t ( a INT, b VARCHAR(10), c INT, PRIMARY KEY(a), KEY(b));
INSERT INTO t SELECT 1,'1',1;
接着运行下面的事务,注意不要提交事务:
BEGIN ;DELETE FROM t WHERE a = 1 ;mysql > SELECT * FROM information_schema . INNODB_TRX_UNDO\G ;*************************** 1. row ***************************trx_id : 303rseg_id : 4undo_rec_no : 0undo _rec_type : TRX_UNDO_DEL_MARK_RECsize : 37space : 0page_no : 308offset : 2721 row in set ( 0.00 sec )
打开共享表空间ibdata1并定位到page_no:308 offset:272的位置,得到如下的内容:
004d4110 01 35 0e 00 0d 00 00 00 00 03 02 e0 83 00 00 01 |. 5. .............|004d4120 33 01 10 04 80 00 00 01 00 0b 00 04 80 00 00 01 | 3. ..............|004d4130 03 01 31 01 10 00 00 00 00 00 00 00 00 00 00 00 |.. 1. ............|
整理后可得:
十六进值 | 说明 |
01 35 | undo log结束位置 |
0e | undo log类型,TRX_UNDO_DEL_MARK_REC |
00 | 记录的info bit信息 |
0d | 表的ID |
00 00 00 03 02 e0 | 记录的隐藏事务ID列 |
83 00 00 01 33 01 10 | 记录的隐藏回滚指针列 |
04 | 主键长度 |
80 00 00 01 | 主键值(a=1) |
00 0b | 之后部分的字节数 |
00 | 列的ID(列a) |
04 | 列占用的字节数 |
80 00 00 01 | 列的值(a=1) |
03 | 列的ID(列b) |
01 | 列占用的字节数 |
31 | 列的值(b=‘1’) |
01 10 | undo log开始位置(0x0135-0x0110=37) |
回滚事务,接着运行下面的例子:
mysql > BEGIN ;Query OK , 0 rows affected ( 0.00 sec )mysql > UPDATE t SET c = 2 WHERE a = 1 ;Query OK , 1 row affected ( 0.00 sec )Rows matched : 1 Changed : 1 Warnings : 0mysql > SELECT * FROM information_schema . INNODB_TRX_UNDO\G ;*************************** 1. row ***************************trx_id : 308rseg_id : 7undo_rec_no : 0undo _rec_type : TRX_UNDO_UPD_EXIST_RECsize : 33space : 0page_no : 310offset : 2721 row in set ( 0.00 sec )
按上述同样的方法整理undo log后可得:
十六进值 | 说明 |
01 31 | undo log结束位置 |
1c | undo log类型,TRX_UNDO_UPD_EXIST_REC |
0d | 表的ID |
00 | 记录的info bit信息 |
00 00 00 03 02 e0 | 记录的隐藏事务ID列 |
83 00 00 01 33 01 10 | 记录的隐藏回滚指针列 |
04 | 主键长度 |
80 00 00 01 | 主键值(a=1) |
01 | update vector 的数量 |
04 | update vector保存的列ID(列c) |
04 | 列占用的字节数 |
80 00 00 01 | 列的值(a=1) |
01 10 | undo log开始位置(0x0135-0x0110=33) |
mysql > BEGIN ;Query OK , 0 rows affected ( 0.00 sec )mysql > UPDATE t SET b = '222' WHERE a = 1 ;Query OK , 1 row affected ( 0.00 sec )Rows matched : 1 Changed : 1 Warnings : 0mysql > SELECT * FROM information_schema . INNODB_TRX_UNDO\G ;*************************** 1. row ***************************trx_id : 30Arseg_id : 8undo_rec_no : 0undo _rec_type : TRX_UNDO_UPD_EXIST_RECsize : 41space : 0page_no : 311offset : 2721 row in set ( 0.00 sec )
可以看到这时undo log的类型还是
TRX_UNDO_UPD_EXIST_REC,但可以发现这时undo type的值为0c而非1c。此外,由于更新了列b,update vector会保存更新时记录列b的值,因此两次操作产生的undo log的量也不同。
何时会产生
TRX_UNDO_UPD_DEL_REC的undo log呢?这个类型表明是在delete mark的记录上进行更新,但是若事务已经提交则delete mark的记录是不允许更改的,其会等待purge线程进行删除。因此产生该类型的undo log发生应发生在同一事务中,如下面的情况:
mysql> BEGIN;Query OK, 0 rows affected (0.00 sec)mysql> DELETE FROM t where a=1;Query OK, 1 row affected (0.00 sec)mysql> INSERT INTO t SELECT 1,'2',2;Query OK, 1 row affected (0.00 sec)Records: 1 Duplicates: 0 Warnings: 0mysql> SELECT * FROM information_schema.INNODB_TRX_UNDO ORDER BY undo_rec_no\G;*************************** 1. row ***************************trx_id: 30Erseg_id: 10undo_rec_no: 0undo_rec_type: TRX_UNDO_DEL_MARK_RECsize: 37space: 0page_no: 314offset: 272*************************** 2. row ***************************trx_id: 30Erseg_id: 10undo_rec_no: 1undo_rec_type: TRX_UNDO_UPD_DEL_RECsize: 46space: 0page_no: 314offset: 3092 rows in set (0.00 sec)
可以看到事务的第二条SQL语句再次插入了主键值为1的记录,并且记录的大小没有发生变化,这意味着可以重用之前已经删除的部分,仅需对其他列的部分进行更新即可,而这就会产生
TRX_UNDO_UPD_DEL_REC的undo log。整理该undo log,最后可得:
十六进值 | 说明 |
01 63 | undo log结束位置 |
0d | undo log类型,TRX_UNDO_UPD_EXIST_REC |
01 | 记录的info bit信息(1表示记录已经被delete mark) |
0d | 表的ID |
00 00 00 03 0e ca | 记录的隐藏事务ID列 |
00 00 01 33 01 10 | 记录的隐藏回滚指针列 |
04 | 主键长度 |
80 00 00 01 | 主键值(a=1) |
02 | update vector 的数量 |
03 | update vector保存的列ID(列b) |
01 | 列占用的字节数 |
31 | 列的值(b='1') |
04 | update vector保存的列ID(列c) |
04 | 列占用的字节数 |
80 00 00 01 | 列的值(c=1) |
00 0b | 之后部分的字节数 |
00 | 列的ID |
04 | 列占用的字节数 |
80 00 00 01 | 列的值(a=1) |
03 | 列的ID |
01 | 列占用的字节数 |
31 | 列的值(c=‘1’) |
01 35 | 开始位置 |
可以看到在某些情况下即便用过数据结构update vector记录发生变化的列,但还是需要记录发生更改的索引列的信息。源码中给出了明确的答案:
/* In the case of a delete marking, and also in the case of an updatewhere any ordering field of any index changes, store the values of allcolumns which occur as ordering fields in any index. This info is usedin the purge of old versions where we use it to build and search thedelete marked index records, to look if we can remove them from theindex tree. */
呼~~~ update undo log分析完了。可以尝试从源码文件trx0rec.c中的函数trx_undo_page_report_modify得到更为直接的答案。若希望自己分析undo log的内容,可以下载InnoSQL并进行尝试。下载地址:https://david-mysql-tools.googlecode.com/files/mysql-5.5.30-v1a-linux-x86_64.tar.gz