MySQL · 引擎特性 · InnoDB 事务系统 (2)

回滚点: 又称为savepoint,事务回滚的时候可以指定回滚点,这样可以保证回滚到指定的点,而不是回滚掉整个事务,对开发者来说,这是一个强大的功能。在InnoDB内部实现中,每打一个回滚点,其实就是保存一下当前的undo_no,回滚的时候直接回滚到这个undo_no点就可以了。

核心数据结构

在分析核心的代码之前,先介绍一下几个核心的数据结构。这些结构贯穿整个事务系统,理解他们对理解整个InnoDB的工作原理也颇有帮助。

trx_t: 整个结构体每个连接持有一个,也就是在创建连接后执行第一个事务开始,整个结构体就被初始化了,后续这个连接的所有事务一直复用里面的数据结构,直到这个连接断开。同时,事务启动后,就会把这个结构体加入到全局事务链表中(trx_sys->mysql_trx_list),如果是读写事务,还会加入到全局读写事务链表中(trx_sys->rw_trx_list)。在事务提交的时候,还会加入到全局提交事务链表中(trx_sys->trx_serial_list)。state字段记录了事务四种状态:TRX_STATE_NOT_STARTED, TRX_STATE_ACTIVE, TRX_STATE_PREPARED, TRX_STATE_COMMITTED_IN_MEMORY。
这里有两个字段值得区分一下,分别是id和no字段。id是在事务刚创建的时候分配的(只读事务永远为0,读写事务通过一个全局id产生器产生,非0),目的就是为了区分不同的事务(只读事务通过指针地址来区分),而no字段是在事务提交前,通过同一个全局id生产器产生的,主要目的是为了确定事务提交的顺序,保证加入到history list中的update undo有序,方便purge线程清理。
此外,trx_t结构体中还有自己的read_view用来表示当前事务的可见范围。分配的insert undo slot和update undo slot。如果是只读事务,read_only也会被标记为true。

trx_sys_t: 这个结构体用来维护系统的事务信息,全局只有一个,在数据库启动的时候初始化。比较重要的字段有:max_trx_id,这个字段表示系统当前还未分配的最小事务id,如果有一个新的事务,直接把这个值作为新事务的id,然后这个字段递增即可。descriptors,这个是一个数组,里面存放着当前所有活跃的读写事务id,当需要开启一个readview的时候,就从这个字段里面拷贝一份,用来判断记录的对事务的可见性。rw_trx_list,这个主要是用来存放当前系统的所有读写事务,包括活跃的和已经提交的事务。按照事务id排序,此外,奔溃恢复后产生的事务和系统的事务也放在上面。mysql_trx_list,这里面存放所有用户创建的事务,系统的事务和奔溃恢复后的事务不会在这个链表上,但是这个链表上可能会有还没开始的用户事务。trx_serial_list,按照事务no(trx_t->no)排序的已经提交的事务。rseg_array,这个指向系统所有可以用的回滚段(undo segments),当某个事务需要回滚段的时候,就从这里分配。rseg_history_len, 所有提交事务的update undo的长度,也就是上文提到的历史链表的长度,具体的update undo链表是存放在这个undo log中以文件指针的形式管理起来。view_list,这个是系统当前所有的readview, 所有开启的readview的事务都会把自己的readview放在这个上面,按照事务no排序。

trx_purge_t: Purge线程使用的结构体,全局只有一个,在系统启动的时候初始化。view,是一个readview,Purge线程不会尝试删除所有大于view->low_limit_no的undolog。limit,所有小于这个值的undolog都可以被truncate掉,因为标记的日志已经被删除且不需要用他们构建之前的历史版本。此外,还有rseg,page_no, offset,hdr_page_no, hdr_offset这些字段,主要用来保存最后一个还未被purge的undolog。

read_view_t: InnDB为了判断某条记录是否对当前事务可见,需要对此记录进行可见性判断,这个结构体就是用来辅助判断的。每个连接都的trx_t里面都有一个readview,在事务需要一致性的读时候(不同隔离级别不同),会被初始化,在读结束的时候会释放(缓存)。low_limit_no,这个主要是给purge线程用,readview创建的时候,会把当前最小的提交事务id赋值给low_limit_no,这样Purge线程就可以把所有已经提交的事务的undo日志给删除。low_limit_id, 所有大于等于此值的记录都不应该被此readview看到,可以理解为high water mark。up_limit_id, 所有小于此值的记录都应该被此readview看到,可以理解为low water mark。descriptors, 这是一个数组,里面存了readview创建时候所有全局读写事务的id,除了事务自己做的变更外,此readview应该看不到descriptors中事务所做的变更。view_list,每个readview都会被加入到trx_sys中的全局readview链表中。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppsgs.html