InnoDB采用表空间 + 回滚段的方式来存储undo log。回滚段定义了UNDO文件的组织方式。
在实现中,可以将UNDO LOG内容与INNODB中的其他数据存储在同一个文件,也可以将UNDO LOG单独存储,这取决于MYSQL initialize时的参数配置。文章中主要以独立UNDO文件来描述。
UNDO TABLE SPACE
UNDO TABLE SPACE指的是UNDO LOG 表空间,每个表空间对应于一个独立的底层物理文件。这个没什么可以更多说的。可以设置undo_tablespaces来决定要创建的UNDO表空间数目(UNDO 文件数量)。该值默认为8。
UNDO ROLLBACK SEGMENT
使用独立表空间存储UNDO LOG,会为每个undo table space创建一定数量回滚段,这受参数srv_rollback_segments控制,默认值为128。将TABLE SPACE划分为SEGMENT的目的是可以让每个事务工作在不同的SEGMENT上而不会互相干扰,以此提高并发度。
使用独立表空间组织形式的UNDO TABLE SPACE的组织可以用下图来表示:
独立UNDO 表空间的存储特点:表空间的第4个page(page-3)内主要存储该表空间内的每个回滚段的header page no,构成一个数组,数组大小为回滚段数量,数组的每一项占据4B
回滚段的header page内有undo log slot数组,如果page大小为16KB,那么该数组大小为1024,数组的每一项代表一个undo log,事务运行需要记录undo log时会寻找当前空闲slot分配undo log
另外,临时表空间也存在自己的回滚段,临时表空间在每次系统启动时都会创建,因此,临时表空间的信息无需持久化,以后可以看到,创建临时表空间时无需记录REDO LOG。
回滚段的HEADER PAGE
Rollback Segment的Header Page布局如下图所示:
一些关键字段:PAGE HEADER:这是INNODB中的通用的PAGE HEADER,占据38字节,这里不作仔细描述
TRX_RSEG_MAX_SIZE:回滚段最大的大小,一般情况下为无限大
TRX_RSEG_HISTORY_SIZE:TRX_RSEG_HISTORY链表大小,即链表上的节点数量
TRX_RSEG_HISTORY:history链表,history链表用途后面会在以后详述。链表上的每个节点是一个地址结构,占据6个字节,包括page_no和offset。通过history list将不再使用的undo log构建成为一个链表,方便以后回收
UNDO LOG SLOT:默认有1024项,每一项对应一个特定的UNDO LOG。
UNDO PAGE 格式
UNDO LOG存储在UNDO PAGE中。UNDO LOG也存在两种类型:insert undo和update undo。其中insert undo记录insert语句中生成的undo log,而update undo则记录update/delete等语句中生成的undo log。每个undo page只存储一种类型的UNDO LOG。
每创建一个trx_undo_t对象时,会先分配一个UNDO SEGMENT,以后该事务上所有的undo log所需page均从该SEGMENT上分配。而只有这个UNDO SEGMENT 的第一个PAGE被称为HEADER PAGE。HEADER PAGE与普通PAGE不一样的地方在于:它上面除了可存储普通的UNDO LOG外,还有UNDO_SEG_HDR信息,包含如下字段:TRX_UNDO_STATE:标记事务状态,有TRX_UNDO_ACTIVE/TRX_UNDO_PREPARED等状态
TRX_UNDO_LAST_LOG:最后一个undo日志的在undo page中的偏移,通过该字段可快速定位最新undo日志的位置
TRX_UNDO_FSEG_HEADER:不确定,占据10个字节大小
TRX_UNDO_PAGE_LIST:该trx_undo_t所有的PAGE构成的双向链表头部。占据16个字节。通过它就可以遍历该trx_undo_t的所有undo page,进而找到所有的undo log内容,设计得还是很巧妙的
普通UNDO PAGE包含了header和body两个大的部分,而header中又包含三个子部分,分别是COMMON PAGE
HEADER和UNDO_PAGE_HDR。其中COMMON PAGE HEADER占据38B,是INNODB中所有PAGE共享,这里暂时不做说明。UNDO_PAGE_HDR包含以下字段:TRX_UNDO_PAGE_TYPE:该undo page记录的undo类型,占据2个字节,目前有TRX_UNDO_INSERT、TRX_UNDO_UPDATE两种类型
TRX_UNDO_PAGE_START:该undo page记录的数据起始位置,undo log从page内的该偏移处写入,就是指图中的Data Region的起始偏移
TRX_UNDO_PAGE_FREE:该undo page记录的数据起始位置,undo log从page内的该偏移处写入,就是指图中的Data Region的起始偏移
TRX_UNDO_PAGE_NODE:这里是前后向指针,用于串联一个trx_undo_t内的所有UNDO PAGE,每个指针大小为6B,总大小12B
UNDO PAGE HEADER占据的大小为18B。
trx_undo_t、header page、undo page三者组成的关系如下图所示:
trx_undo_t内的hdr_page_no记录了header undo page的编号,last_page_no记录了正在使用的undo page的编号。header page内的TRX_UNDO_FSEG_HEADER为undo page链表的头部,每个undo page通过其TRX_UNDO_PAGE_NODE将自身串联在链表中。
UNDO LOG格式
不包含XID信息
UNDO LOG 由UNDO LOG HEADER与UNDO LOG DATA构成,一个UNDO PAGE内的多个UNDO LOG通过前后向指针关联起来。
其中UNDO LOG HEADER包含如下字段:TRX_UNDO_DEL_MASKS:暂时不清楚含义
TRX_UNDO_TRX_ID:产生该UNDO LOG的事务id
TRX_UNDO_LOG_START:UNDO LOG DATA所在偏移
TRX_UNDO_XID_EXISTS:UNDO LOG HEADER中是否包含XID信息,本例中演示的场景是不含XID信息的头部(字段值为FALSE),也是老版本的格式,新版本中在头部包含了XID,会在接下来分析
TRX_UNDO_NEXT_LOG:该UNDO PAGE中后一个UNDO LOG所在偏移
TRX_UNDO_PREV_LOG:该UNDO PAGE中前一个UNDO LOG所在偏移
RESERVED:预留了不少字段,暂时不明白其具体含义
构造UNDO LOG HEADER见函数trx_undo_header_create
包含XID信息的UNDO LOG格式
主要区别是在原有老版本基础上,在UNDO LOG HEADER中新增XID相关字段:TRX_UNDO_XA_FORMAT:不清楚含义
TRX_UNDO_TRID_LEN:XID长度
TRX_UNDO_XA_BQUAL_LEN:不清楚含义
TRX_UNDO_XA_XID:XID内容
在老版本格式基础上预留XID字段信息可见函数 trx_undo_header_add_space_for_xid。创建UNDO LOG见函数trx_undo_create。
UNDO LOG DATA格式
insert undo record格式:
update undo record格式: