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

在奔溃恢复后,也就是所有的前滚redo都应用完后,数据库需要做undo回滚,至于哪些事务需要提交,哪些事务需要回滚,这取决于undolog和binlog的状态。启动阶段,事务相关的代码逻辑主要在函数trx_sys_init_at_db_start中,简单分析一下。

首先创建管理undo segment的最小堆,堆中的元素是每个undo segment提交最早的事务id和相应undo segment的指针,也就是说通过这个元素可以找到这个undo segment中最老的未被Purge的undo。通过这个最小堆,可以找到所有undo segment中最老未被Purge的undo,方便Purge线程操作。

创建全局的活跃读写事务id数组。主要是给readview使用。

初始化所有的undo segment。主要是从磁盘读取undolog的内容,构建内存中的undo slot和undo segment,同时也构建每个undo segment中的history list,因为如果是fast shutdown,被标记为删除的记录可能还没来得及被彻底清理。此外,也构建每个undo segment中的inset_undo_list和update_undo_list,理论上来说,如果数据库关闭的时候所有事务都正常提交了,这两个链表都为空,如果数据库非正常关闭,则链表非空(trx_undo_mem_create_at_db_start, trx_rseg_mem_create)。

从系统页里面读取max_trx_id,然后加上TRX_SYS_TRX_ID_WRITE_MARGIN来保证trx_id不会重复,即使在很极端的情况下。

遍历所有的undo segment,针对每个undo segment,分别遍历inset_undo_list和update_undo_list,依据undo的状态来复活事务。

insert/update undo的处理逻辑:如果undolog上的状态是TRX_UNDO_ACTIVE,则事务也被设置为TRX_STATE_ACTIVE,如果undolog上的状态是TRX_UNDO_PREPARED,则事务也被设置为TRX_UNDO_PREPARED(如果force_recovery不为0,则设置为TRX_STATE_ACTIVE)。如果undolog状态是TRX_UNDO_CACHED,TRX_UNDO_TO_FREE,TRX_UNDO_TO_PURGE,那么都任务事务已经提交了(trx_resurrect_insert和trx_resurrect_update)。

除了从undolog中复活出事务的状态信息,还需要复活出当前的锁信息(trx_resurrect_table_locks),此外还需要把事务trx_t加入到rw_trx_list中。

所有事务信息复活后,InnoDB会做个统计,告诉你有多少undo需要做,因此可以在错误日志中看到类似的话: InnoDB: 120 transaction(s) which must be rolled back or cleaned up. InnoDB: in total 20M row operations to undo。

如果事务中操作了数据字典,比如创建删除表和索引,则这个事务会在奔溃恢复结束后直接回滚,这个是个同步操作,会延长奔溃恢复的时间(recv_recovery_from_checkpoint_finish)。如果事务中没有操作数据字典,则后台会开启一个线程,异步回滚事务,所以我们常常发现,在数据库启动后,错误日志里面依然会有很多事务正在回滚的信息。

事务运维相关命令和参数

首先介绍一下information_schema中的三张表: innodb_trx, innodb_locks和innodb_lock_waits。由于这些表几乎需要查询所有事务子系统的核心数据结构,为了减少查询对系统性能的影响,InnoDB预留了一块内存,内存里面存了相关数据的副本,如果两次查询的时间小于0.1秒(CACHE_MIN_IDLE_TIME_US),则访问的都是同一个副本。如果超过0.1秒,则这块内存会做一次更新,每次更新会把三张表用到的所有数据统一更新一遍,因为这三张表经常需要做表连接操作,所以一起更新能保证数据的一致性。这里简单介绍一下innodb_trx表中的字段,另外两张表涉及到事物锁的相关信息,由于篇幅限制,后续有机会在介绍。
trx_id: 就是trx_t中的事务id,如果是只读事务,这个id跟trx_t的指针地址有关,所以可能是一个很大的数字(trx_get_id_for_print)。
trx_weight: 这个是事务的权重,计算方法就是undolog数量加上事务已经加上锁的数量。在事务回滚的时候,优先选择回滚权重小的事务,有非事务引擎参与的事务被认为权重是最大的。
trx_rows_modified:这个就是当前事务已经产生的undolog数量,每更新一条记录一次,就会产生一条undo。
trx_concurrency_tickets: 每次这个事务需要进入InnoDB层时,这个值都会减一,如果减到0,则事务需要等待(压力大的情况下)。
trx_is_read_only: 如果是以start transaction read only启动事务的,那么这个字段是1,否则为0。
trx_autocommit_non_locking: 如果一个事务是一个普通的select语句(后面没有跟for update, share lock等),且当时的autocommit为1,则这个字段为1,否则为0。
trx_state: 表示事务当前的状态,只能有RUNNING, LOCK WAIT, ROLLING BACK, COMMITTING这几种状态, 是比较粗粒度的状态。
trx_operation_state: 表示事务当前的详细状态,相比于trx_state更加详细,例如有rollback to a savepoint, getting list of referencing foreign keys, rollback of internal trx on stats tables, dropping indexes等。

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

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