MySQL 核心模块揭秘 | 事务提交了,Undo 日志怎么办?

数据库 MySQL
如果 Insert Undo 段的状态为 TRX_UNDO_TO_FREE,先释放 Undo 表空间中该 Insert Undo 段及它管理的 Undo 页,再释放 Undo 段的内存对象。

目录

  • 1. 修改 Insert Undo 段状态
  • 2. 生成事务提交号
  • 3. 回滚段加入 purge 队列
  • 4. 处理 Update Undo 段

4.1 Undo 段状态

4.2 Undo 日志页数量

4.3 Undo 日志组

4.4 Undo 段链表

  • 5. 清理 Insert Undo 段
  • 6. 总结

1. 修改 Insert Undo 段状态

事务提交过程中,首先要处理的 Undo 相关逻辑,就是修改 Insert Undo 段的状态:

  • 如果事务分配了用户普通表 Insert Undo 段,修改该 Undo 段的状态。
  • 如果事务分配了用户临时表 Insert Undo 段,修改该 Undo 段的状态。

根据 Insert Undo 段是否能直接被复用,Insert Undo 段的状态会被修改为 TRX_UNDO_CACHED 或者 TRX_UNDO_TO_FREE。

如果 Insert Undo 段只管理了一个 Undo 页,并且该 Undo 页已使用空间小于四分之三,Undo 段的状态被修改为 TRX_UNDO_CACHED,表示 Undo 段可以缓存起来直接复用。否则,Undo 段的状态被修改为 TRX_UNDO_TO_FREE,表示 Undo 段以及它管理的 Undo 页需要被释放。

2. 生成事务提交号

如果事务分配了用户普通表 Update Undo 段,或者用户临时表 Update Undo 段,事务提交过程中,需要生成事务提交号。事务提交完成之后,purge 线程会根据这个事务提交号,决定什么时候清理该 Update Undo 段管理的 Undo 页中的 Undo 日志。

和事务 ID 一样,事务提交号也来源于事务子系统(trx_sys)的 next_trx_id_or_no 属性。

事务启动时,直接获取 trx_sys->next_trx_id_or_no 属性的值,作为事务 ID,然后该属性值加 1。事务提交时,直接获取 trx_sys->next_trx_id_or_no 属性的值,作为事务提交号,然后该属性值加 1。这意味着事务 ID 和事务提交号由同一个流水线生产,同一个事务的提交号总是大于事务 ID。

生成的事务提交号会保存到事务对象(trx)的 no 属性中。

生成事务提交号之后,当前正在提交的事务对象(trx)会加入事务子系统(trx_sys)的 serialisation_list 链表的末尾。这个链表中的所有事务,都是正在提交的事务。更严格的来说,这些事务都是已经生成了事务提交号,但是还没有提交完成的事务。

3. 回滚段加入 purge 队列

如果事务分配了 Update Undo 段,该 Undo 段所属的回滚段需要加入到 purge 队列中,表示该回滚段下有需要 purge 线程清理的 Undo 日志。

如前所述,回滚段会按需加入 purge 队列:

  • 如果用户普通表回滚段下分配了 Update Undo 段,并且该回滚段目前不在 purge 队列中,则加入 purge 队列。
  • 如果用户临时表回滚段下分配了 Update Undo 段,并且该回滚段目前不在 purge 队列中,则加入 purge 队列。

InnoDB 给同一个事务分配的的用户普通表回滚段和用户临时表回滚段,如果都需要加入 purge 队列,不能各自为战,而是打包加入。

这个包怎么打?

InnoDB 会创建一个 TrxUndoRsegs 对象,这个对象有个 m_rsegs 属性,是个数组。

如果用户普通表回滚段需要加入 purge 队列,先加入到 m_rsegs 数组中。

如果用户临时表回滚段需要加入 purge 队列,也加入到 m_rsegs 数组中。

然后,事务对象(trx)的 no 属性中保存的事务提交号,也保存一份到 TrxUndoRsegs 对象的 m_trx_no 属性中。

打完包之后,TrxUndoRsegs 对象会被加入 purge 队列。

为了逻辑统一,如果事务只分配了用户普通表回滚段、用户临时表回滚段两者之一,回滚段也会打包成 TrxUndoRsegs 再加入 purge 队列。

4. 处理 Update Undo 段

用户普通表 Update Undo 段和用户临时表 Update Undo 段的处理逻辑一样。下面以用户普通表 Update Undo 段为例,介绍事务提交过程中 Update Undo 段需要进行的操作。

4.1 Undo 段状态

如果事务分配了用户普通表 Update Undo 段,现在需要修改它的状态了。和 Insert Undo 段一样,满足条件的 Update Undo 段也可以被缓存起来直接复用。

如果 Update Undo 段只管理了一个 Undo 页,并且该 Undo 页已使用空间小于四分之三,这个 Update Undo 段可以被缓存起来直接复用,它的状态会被修改为 TRX_UNDO_CACHED。否则该 Undo 段不能被复用,它的状态会被修改为 TRX_UNDO_TO_PURGE,表示等待 purge 线程清理 Update Undo 段管理的 Undo 页中的 Undo 日志。

4.2 Undo 日志页数量

对于状态为 TRX_UNDO_TO_PURGE 的 Update Undo 段,回滚段首页中保存着该 Undo 段首页的页号的小格子(Undo Slot)的值会被修改为 4294967295(代码里为 FIL_NULL),也就解除了回滚段和该 Undo 段的关系。

Update Undo 段管理的 Undo 页的数量,会累加到回滚段头信息的 TRX_RSEG_HISTORY_SIZE 属性中,这个属性表示回滚段的 history 链表中所有 Undo 日志组占用的不会再写入 Undo 日志的 Undo 页的数量之和。

4.3 Undo 日志组

本小节介绍的内容,状态为 TRX_UNDO_CACHED 和 TRX_UNDO_TO_PURGE 的 Update Undo 段都需要操作。

当前 Update Undo 段中,正在提交的事务产生的 Undo 日志所在的 Undo 日志组,会加入回滚段的 history 链表的头部,等待 purge 线程清理其中的 Undo 日志。

给事务子系统(trx_sys)的 rseg_history_len 属性值加 1,表示回滚段的 history 链表中等待 purge 线程清理 Undo 日志的 Undo 日志组又增加了一组。

代码里实现的 rseg_history_len 加 1 的过程有一点点复杂,这里描述的是结果,也就是 Update Undo 段中一个 Undo 日志组加入了 history 链表,rseg_history_len 就会加 1。

rseg_history_len 加 1 之后,还会判断相加的结果是否大于阈值。如果大于,意味着回滚段的 history 链表中等待清理的 Undo 日志组有点多。此时,如果 purge 线程处于休眠状态,会唤醒 purge 线程开始清理 Undo 日志。

接着还要把事务对象(trx)的 no 属性中保存的事务提交号,写入回滚段头信息的 TRX_RSEG_MAX_TRX_NO 属性中、Undo 日志组头信息的 TRX_UNDO_TRX_NO 属性中。

如果这个 Undo 日志组中既不包含 Delete 或者 Update 操作标记删除记录产生的 Undo 日志,也不包含修改溢出字段产生的 Undo 日志,还会把 Undo 日志组头信息的 TRX_UNDO_DEL_MARKS 属性值修改为 false,purge 线程清理 Undo 日志过程中读取到这组 Undo 日志时,就知道不需要执行物理删除表中记录的操作。

如果管理当前 Update Undo 段的回滚段不在 purge 队列中,会加入 purge 队列。否则,不需要重复加入。purge 线程清理完回滚段的 history 链表中一个 Undo 日志组的所有 Undo 日志之后,接下来就会清理下一组。

回滚段的 history 链表中,Undo 日志组按照自己头信息的 TRX_UNDO_TRX_NO 属性中保存的事务提交号,由小到大串连起来。事务提交号最小的 Undo 日志组在 history 链表末尾,事务提交号最大的 Undo 日志组在 history 链表头部。

purge 线程清理时,先清理事务提交号小的 Undo 日志组中的 Undo 日志,再清理事务提交号大的 Undo 日志组中的 Undo 日志。

4.3 Undo 段链表

前面那些操作完成之后,就进入收尾阶段了。Update Undo 段会从回滚段的 update_undo_list 链表中移除。

如果 Update Undo 段的状态为 TRX_UNDO_CACHED,还会加入回滚段的 update_undo_cached 链表头部,等待复用。

如果 Update Undo 段的状态为 TRX_UNDO_TO_PURGE,则释放它的内存对象。Undo 表空间中该 Undo 段及它管理的 Undo 页都不会释放,需要等到 purge 线程清理完 Undo 日志之后才能释放。

5. 清理 Insert Undo 段

前面已经确定了 Insert Undo 段的状态,现在是时候根据状态处理 Insert Undo 段了。

首先,从回滚段的 insert_undo_list 链表中删除 Insert Undo 段。

然后,如果 Insert Undo 段的状态为 TRX_UNDO_CACHED,把它加入到回滚段的 insert_undo_cached 链表头部。

如果 Insert Undo 段的状态为 TRX_UNDO_TO_FREE,先释放 Undo 表空间中该 Insert Undo 段及它管理的 Undo 页,再释放 Undo 段的内存对象。

6. 总结

事务提交过程中,Undo 相关的流程如下:

  • 修改 Insert Undo 段的状态为 TRX_UNDO_CACHED 或者 TRX_UNDO_TO_FREE。
  • 生成事务提交号。
  • 把管理 Update Undo 段的回滚段加入到 purge 队列中。
  • 从回滚段的 update_undo_list 链表中移除 Update Undo 段。可以被缓存的 Update Undo 段,还需要加入 update_undo_cached 链表。
  • 从回滚段 insert_undo_list 链表中移除 Insert Undo 段。可以被缓存的 Insert Undo 段,还需要加入 insert_undo_cached 链表。
责任编辑:武晓燕 来源: 爱可生开源社区
相关推荐

2024-04-03 08:20:53

MySQL核心模块

2024-06-05 11:49:33

2024-05-15 09:05:42

MySQL核心模块

2022-07-05 14:19:30

Spring接口CGLIB

2024-08-28 08:50:11

MySQL核心模块

2024-03-27 13:33:00

MySQLInnoDB事务

2022-07-05 11:48:47

MySQL死锁表锁

2024-04-22 08:17:23

MySQL误删数据

2020-11-02 08:21:50

Git办法代码

2024-08-07 14:58:00

MySQL释放锁核心模块

2022-07-06 08:02:51

undo 日志数据库

2022-12-20 08:46:41

MySQL主从复制

2024-10-30 10:38:08

2024-10-16 11:11:51

隔离InnoDB死锁

2024-05-29 10:17:01

2025-01-15 13:19:09

MySQL日志事务

2024-11-11 00:00:06

MySQLID数据类型

2024-09-04 08:44:18

MySQL核心模块

2019-10-12 09:50:46

Redis内存数据库

2018-01-28 20:39:39

戴尔
点赞
收藏

51CTO技术栈公众号