采用round-robin的轮询方式来赋予回滚段给事务,如果回滚段被标记为skip_allocation(这个undo tablespace太大了,purge线程需要对其进行truncate操作),则跳到下一个;
选择一个回滚段给事务后,会将该回滚段的rseg->trx_ref_count递增,这样该回滚段所在的undo tablespace文件就不可以被truncate掉;
临时表回滚段被赋予trx->rsegs->m_noredo,普通读写操作的回滚段被赋予trx->rsegs->m_redo;如果事务在只读阶段使用到临时表,随后转换成读写事务,那么会为该事务分配两个回滚段。
使用回滚段当产生数据变更时,我们需要使用Undo log记录下变更前的数据以维护多版本信息。insert 和 delete/update 分开记录undo,因此需要从回滚段单独分配Undo slot。
入口函数:trx_undo_report_row_operation
流程如下:
判断当前变更的是否是临时表,如果是临时表,则采用临时表回滚段来分配,否则采用普通的回滚段;
临时表操作记录undo时不写redo log;
操作类型为TRX_UNDO_INSERT_OP,且未分配insert undo slot时,调用函数trx_undo_assign_undo进行分配;
操作类型为TRX_UNDO_MODIFY_OP,且未分配Update undo slot时,调用函数trx_undo_assign_undo进行分配。
我们来看看函数trx_undo_assign_undo的流程:
首先总是从cahced list上分配trx_undo_t (函数trx_undo_reuse_cached,当满足某些条件时,事务提交时会将其拥有的trx_undo_t放到cached list上,这样新的事务可以重用这些undo 对象,而无需去扫描回滚段,寻找可用的slot,在后面的事务提交一节会介绍到);
对于INSERT,从trx_rseg_t::insert_undo_cached上获取,并修改头部重用信息(trx_undo_insert_header_reuse)及预留XID空间(trx_undo_header_add_space_for_xid)
对于DELETE/UPDATE,从trx_rseg_t::update_undo_cached上获取, 并在undo log hdr page上创建新的Undo log header(trx_undo_header_create),及预留XID存储空间(trx_undo_header_add_space_for_xid)
获取到trx_undo_t对象后,会从cached list上移除掉。并初始化trx_undo_t相关信息(trx_undo_mem_init_for_reuse),将trx_undo_t::state设置为TRX_UNDO_ACTIVE
如果没有cache的trx_undo_t,则需要从回滚段上分配一个空闲的undo slot(trx_undo_create),并创建对应的undo页,进行初始化;
一个回滚段可以支持1024个事务并发,如果不幸回滚段都用完了(通常这几乎不会发生),会返回错误DB_TOO_MANY_CONCURRENT_TRXS
每一个Undo log segment实际上对应一个独立的段,段头的起始位置在UNDO 头page的TRX_UNDO_SEG_HDR+TRX_UNDO_FSEG_HEADER偏移位置(见下图)
已分配给事务的trx_undo_t会加入到链表trx_rseg_t::insert_undo_list或者trx_rseg_t::update_undo_list上;
如果是数据词典操作(DDL)产生的undo,主要是表级别操作,例如创建或删除表,还需要记录操作的table id到undo log header中(TRX_UNDO_TABLE_ID),同时将TRX_UNDO_DICT_TRANS设置为TRUE。(trx_undo_mark_as_dict_operation)。
总的来说,undo header page主要包括如下信息:
入口函数:trx_undo_report_row_operation
当分配了一个undo slot,同时初始化完可用的空闲区域后,就可以向其中写入undo记录了。写入的page no取自undo->last_page_no,初始情况下和hdr_page_no相同。
对于INSERT_UNDO,调用函数trx_undo_page_report_insert进行插入,记录格式大致如下图所示:
对于UPDATE_UNDO,调用函数trx_undo_page_report_modify进行插入,UPDATE UNDO的记录格式大概如下图所示:
在写入的过程中,可能出现单页面空间不足的情况,导致写入失败,我们需要将刚刚写入的区域清空重置(trx_undo_erase_page_end),同时申请一个新的page(trx_undo_add_page) 加入到undo log段上,同时将undo->last_page_no指向新分配的page,然后重试。
完成Undo log写入后,构建新的回滚段指针并返回(trx_undo_build_roll_ptr),回滚段指针包括undo log所在的回滚段id、日志所在的page no、以及page内的偏移量,需要记录到聚集索引记录中。