一、概念解析
1、undo log基本
undo log是innodb事务中特有的结构,它的作用有两个:
- 一是进行事务回滚(原子性),旧数据先放到undo log中,等rollback时再将旧数据里的数据回滚回来;
- 二是mvcc(隔离性),非阻塞读。
undo能够避免脏读:在操作数据之前,将数据备份到undo页中,然后在进行数据的修改,不能看到其他回话未提交的数据,当要读取被修改页数据行时,会指向备份在undo页中的数据,避免脏读。
- undo回滚段头物理存储在ibdata1文件中第6个页位置。
- undo log存放在数据库内部的一个特殊段中,这个段称为undo段。undo段位于共享表空间中。回滚段实际上是一种 undo 文件组织方式。
在innodb存储引擎中有rollback segment,每个回滚段记录了1024个undo log segment(slot),每个undo log segment段中进行undo页的申请。
从1.1版本开始innodb支持最大128个rollback segment,所以理论上可以支持的最大的事务上限为128*1024。
在1.2版本开始,可以通过参数对rollback segment进行设置,参数有:
参数名称 | 含义 |
innodb_undo_directory | 设置回滚段文件所在的路径 |
innodb_undo_logs | 设置回滚段的个数 |
innodb_undo_tablespaces | 设置构成回滚段文件的数量 |
也就是说:
回滚段可以存放在共享表空间以外的地方,即可以设置独立表空间,innodb_undo_directory的默认值为“.”,表示当前innodb存储引擎的目录。
回滚段的个数默认值为128。
设置回滚段文件的数量后,回滚段可以较为平均地分布在多个文件中,在路径下可以看到undo为前缀的文件,该文件代表回滚段文件。
可用show variables like “innodb_undo%”;查询:
重新初始化时指定innodb_undo_tablespaces=n来设置,设置好后会在data目录下生成undo独立表空间(大小10m):
2、事务系统头transactionsystemheader
回滚段的管理,在innodb共享表空间中,用第6个页面(5号)来管理的,这个页面专门用来存储事务系统相关信息的,源代码位于trx0sys.h,它的格式如下:
这三个参数的含义分别为:
参数名称 | 含义 |
trx_sys_trx_id_store | 存储事务号 |
trx_sys_fseg_header | 存储事务段信息 |
trx_sys_rsegs | 数组,innodb有128个rollback segment |
3、回滚段头rollbacksegmentheader
对于每一个回滚段,即上面trx_sys_rsegs数组中的一个元素,也有其自己的存储格式,代码中的宏定义位于trx0rseg.h,格式如下:
这五个信息的含义如下:
参数名称 | 含义 |
trx_rseg_max_size | 所有undo段页面之和 |
trx_rseg_history_size | 需要purge的回滚段页面数 |
trx_rseg_history | 存储history list的链表首地址 |
trx_rseg_fseg_header | 存储回滚段的inode位置信息 |
tex_rseg_undo_slots | 数组,1024,每个元素是一个页面号 |
这五个信息存储了一个回滚段的信息,最后一个位置的数组,用来真正存储回滚段的位置。
这五个信息是从页面偏移38的位置开始存储,在trx_sys宏定义中可以查看:
4、回滚页头undologpageheader
定义在trx0undo.h,事务undo日志页面头偏移量:
其中各个参数的含义为:
参数名称 | 含义 |
trx_undo_page_type | 类型,trx_undo_insert\update |
trx_undo_page_start | 开始存储undo日志的位置 |
trx_undo_page_free | 结束标志 |
trx_undo_page_node | 双向链表的指针 |
第一个页在undo log段的回滚日志段头:
其中各个参数的含义为:
参数名称 | 含义 |
trx_undo_state | undo段五种状态,包括trx_undo_active\cached\to_free\to_purge\prepared |
trx_undo_last_log | 最后一个undo日志的偏移位置 |
trx_undo_fseg_header | undo段inode信息 |
trx_undo_page_list | 段链表的首地址 |
其中undo log段的状态有五种:
5、回滚日志头undologheader
这是回滚日志的头,在第一页的update undo log中有几个undo log headers。同样定义在trx0undo.h中,它的宏定义如下:
整理并解释其中的含义:
参数名称 | 含义 |
trx_undo_trx_id | 存储当前undo日志对应事务的事务id |
trx_undo_trx_no | 事务序列号 |
trx_undo_del_marks | 删除记录标记 |
trx_undo_log_start | 存储当前页面中第一个undo日志的开始位置 |
trx_undo_xid_exists | 标记的当前日 志中有没有包含xid事务 |
trx_undo_dict_trans | 标记当前事务是不是ddl,在回滚时判断如何操作 |
trx_undo_table_id | 表id |
trx_undo_next_log | 链接指向下一个undo日志 |
trx_undo_prev_log | 指向上一个undo日志,构成双向链表 |
trx_undo_history_node | 存储history list中的双向链表,链表首地址 |
6、两种undolog格式
undo log格式有两种:
- insert undo log
- update undo log
其中,insert操作会产生insert undo log。因为隔离性的要求,insert操作只对事务本身可见,对其他事务不可见。并且undo log可以在事务提交后直接删除,不需要进行purge操作。
update和delete操作都会产生update undo log。对于update操作很简单,就是将旧版本的值修改为新版本的值,并且将旧版本的记录保存到undo log中;对于delete操作完后并不是真正的删除该行数据,而是将该行的数据存在undo log中,并且将标记位delete置为true,以备回滚时可以根据标记位进行逆操作,这样就能够找回之前的旧数据,起到回滚的作用。
在delete中又分为三种情况:
purge线程两个主要作用是:清理undo页和清除page里面带有delete_bit标识的数据行。在innodb中,事务中的delete操作实际上并不是真正的删除掉数据行,而是在记录上标识delete_bit,而不是真正删除记录,真正的删除工作需要后台purge线程去完成。
下面是两种undo log记录的具体的结构:
二、数据结构
与undo log相关的数据结构有trx_sys_t、trx_rsegs_t 、trx_t、trx_rseg_t、trx_undo_t、trx_undo_ptr_t等结构,其中比较重要的结构有两个:trx_rseg_t和trx_undo_t。trx_rseg_t保存回滚段的信息,trx_undo_t保存关于回滚日志的信息。
通过两种方式找到回滚日志:
结论:
1、从全局变量trx_sys中的回滚段数组rseg_array中的回滚链表update-undo_list中指针(trx_sys->rseg_array[n]->update_undo_list->start)以及当前事务trx中的回滚段指针rsegs中的slot槽区m_redo中的undo log(trx->rsegs->m_redo->update_undo)都可以找到相应的回滚段中的回滚日志。
2、事务id、回滚段id、page_no递增。
1、trx_rseg_t
回滚段的内存对象的类型是一个叫trx_rseg_t的结构,它定义在trx0rseg.h文件中,它的各成员有:
成员变量 | 解释 |
ulint id; | 回滚段id |
rsegmutex mutex; | 回滚段mutex,保护 |
ulint space; | 回滚段头空间 |
ulint page_no; | 回滚段页码 |
page_size_t page_size; | 页面大小 |
ulint max_size; | 允许的最大页大小 |
ulint curr_size; | 当前页的大小 |
ut_list_base_node_t(trx_undo_t) update_undo_list; | update回滚日志链表 |
ut_list_base_node_t(trx_undo_t) update_undo_cached; | 为快速重用而缓存的update回滚日志链表 |
ut_list_base_node_t(trx_undo_t) insert_undo_list; | insert回滚日志的字段 |
ut_list_base_node_t(trx_undo_t) insert_undo_cached; | 为快速重用而缓存的insert undo log链表 |
ulint last_page_no; | 最后一个尚未清除的日志页码 |
ulint last_offset; | 最后一个尚未清除的日志头的字节偏移量 |
trx_id_t last_trx_no; | 最后一个尚未清除的日志的事务号 |
ibool last_del_marks; | 判断最后一个尚未清除的日志是否要清除 |
ulint trx_ref_count; | 跟踪rseg分配事务的参考计数器 |
bool skip_allocation; | 如果为真,则跳过分配此rseg |
update\insert_undo_list 用于保存产生的回滚日志,update\insert_undo_cached用于快速重用而缓存的回滚日志链表。当确定回滚日志无用时,会将回滚日志从update\insert_undo_list中摘除,放到相应的cached链表中。
2、trx_undo_t
回滚日志的内存对象的类型是一个叫trx_undo_t的结构,它定义在trx0undo.h文件中,它的各成员有:
成员变量 | 解释 |
ulint id; | 回滚段内的回滚日志槽号(slot) |
ulint type; | 类型。trx_undo_insert\update |
ulint state; | 相应的回滚日志段的状态 |
ibool del_marks; | 删除标记 |
trx_id_t trx_id; | 事务id |
xid xid; | open xa事务识别 |
ibool dict_operation; | 是否是dict操作 |
table_id_t table_id; | 设置表id |
trx_rseg_t* rseg; | 回滚日志所属的rseg回滚段 |
ulint space; | 放置回滚日志的空间id |
page_size_t page_size; | 回滚日志中标题页的大小 |
ulint hdr_page_no; | 回滚日志中标题页的页码 |
ulint hdr_offset; | 页面上回滚日志的header偏移量 |
ulint last_page_no; | 登录日志中最后一页的页码 |
ulint size; | 当前页的大小 |
ulint empty; | 回滚日志记录堆栈当前是否为空 |
ulint top_page_no; | 链接最近撤消日志记录的页码 |
ulint top_offset; | 最新回滚记录的偏移量 |
undo_no_t top_undo_no; | 最新的回滚记录的编号 |
buf_block_t* guess_block; | 猜测可能位于首页的缓冲区 |
ulint withdraw_clock; | 存储guess_block时缓冲池的退出时钟值 |
ut_list_node_t(trx_undo_t) undo_list; | 回滚段中的回滚日志对象链表 |
回滚段指针包括回滚段的id、日志所在的page no、以及page内偏移量。通过回滚段的指针就能够找到这个回滚段,通过回滚日志的槽号、页码、回滚记录编号和偏移量就能定位到这条回滚日志。
undo_list用于将此类型的undo log链接到undo log list中。
ut_list_node_t(trx_undo_t) undo_list链表中有两个指针,分别为prev和next,用于将此undo log链接到undo log list中。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论