服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - Mysql - MySQL 核心模块揭秘,你看明白了吗?

MySQL 核心模块揭秘,你看明白了吗?

2024-03-27 15:17爱可生开源社区 Mysql

为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。

1. 关于缓存 undo 段

为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。

只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。

条件 1:undo 段中只有一个 undo 页。

条件 2:这个唯一的 undo 页中,已经使用的的空间必须小于数据页大小的四分之三。以默认大小 16K 的 undo 页为例,undo 页中已经使用的空间必须小于 12K。

如果 insert undo 段满足缓存条件,它会加入回滚段的 insert_undo_cached 链表头部。

如果 update undo 段满足缓存条件,它会加入回滚段的 update_undo_cached 链表头部。

2. InnoDB 提交事务

二阶段提交过程中,commit 阶段的 flush 子阶段,把 prepare 阶段及之前产生的 redo 日志都刷盘了,把事务执行过程中产生的 binlog 日志都写入 binlog 日志文件了。

sync 子阶段根据系统变量 sync_binlog 的值决定是否触发操作系统把 binlog 日志刷盘。

前两个子阶段,都只处理了日志,不涉及 InnoDB 的事务。这两个阶段完成之后,InnoDB 的事务还没有提交,事务还处于准备提交状态(TRX_STATE_PREPARED)。

commit 子阶段才会真正提交 InnoDB 的事务,这个阶段完成之后,事务就提交完成了。

commit 子阶段提交 InnoDB 的事务,要做的事情有这些:

  • 修改 insert undo 段的状态。
  • 生成事务提交号,用于 purge 线程判断是否能清理某些 update undo 日志组中的 undo 日志。
  • 修改 update undo 段的状态。
  • 把 update undo 段中的 undo 日志组加入回滚段的 history list 链表。purge 线程会从这个链表中获取需要清理的 update undo 日志组。
  • 把事务状态修改为 TRX_STATE_COMMITTED_IN_MEMORY。
  • 释放事务执行过程中 InnoDB 给表或记录加的锁。
  • 重新初始化事务对象,以备当前线程后续使用。

2.1 修改 insert undo 段状态

如果事务插入记录到用户普通表,InnoDB 会为事务分配一个 insert undo 段。

如果事务插入记录到用户临时表,InnoDB 会为事务分配另一个 insert undo 段。

InnoDB 可能会给事务分配 0 ~ 2 个 insert undo 段。commit 子阶段会修分配给事务的所有 insert undo 段的状态。

如果 insert undo 段满足缓存条件,它的状态会被修改为 TRX_UNDO_CACHED,否则,它的状态会被修改为 TRX_UNDO_TO_FREE。

事务提交完成之后,InnoDB 会根据状态缓存或者释放 insert undo 段。

2.2 生成事务提交号

事务提交号是事务对象的 no 属性,通常用 trx->no 表示。

代码里,对事务提交号的注释是 transaction serialization number,直译成中文应该称为事务序列号,或者事务串行号。

因为 trx->no 是在事务提交时生成的,我们还是把它称为事务提交号更容易理解一些。

只有 update undo 段需要事务提交号。purge 线程清理 update undo 日志时,会根据 update undo 段的 undo 日志组中保存的事务提交号,决定是否能清理这个 undo 日志组中的 undo 日志。

修改 update undo 段的状态之前,InnoDB 会生成事务提交号,保存到事务对象的 no 属性中。

// storage/innobase/trx/trx0trx.cc
static inline bool trx_add_to_serialisation_list(trx_t *trx) {
  ...
  trx->no = trx_sys_allocate_trx_no();
  ...
}

trx_sys_allocate_trx_no() 调用 trx_sys_allocate_trx_id_or_no() 生成事务提交号。

// storage/innobase/include/trx0sys.ic
// 生成事务 ID
inline trx_id_t trx_sys_allocate_trx_id() {
  ut_ad(trx_sys_mutex_own());
  return trx_sys_allocate_trx_id_or_no();
}
// 生成事务提交号
inline trx_id_t trx_sys_allocate_trx_no() {
  ut_ad(trx_sys_serialisation_mutex_own());
  return trx_sys_allocate_trx_id_or_no();
}

从上面的代码可以看到,生成事务 ID 和事务提交号调用的是同一个方法,trx_sys_allocate_trx_id_or_no() 的代码如下:

// storage/innobase/include/trx0sys.ic
inline trx_id_t trx_sys_allocate_trx_id_or_no() {
  ...
  // trx_sys_allocate_trx_id_or_no() 每次被调用
  // trx_sys->next_trx_id_or_no 加 1
  // trx_id 保存的是加 1 之前的值
  trx_id_t trx_id = trx_sys->next_trx_id_or_no.fetch_add(1);
  ...
  return trx_id;
}

trx_sys->next_trx_id_or_no 保存的是下一个事务 ID 或事务提交号,具体是哪个,取决于是生成事务 ID 还是生成事务提交号先调用 trx_sys_allocate_trx_id_or_no()。

也就是说,事务 ID 和事务提交号是同一条流水线上生产出来的。我们以 trx 1 和 trx 2 两个事务为例,来说明生成事务 ID 和事务提交号的流程。

假设此时 trx_sys->next_trx_id_or_no 的值为 100,trx 1、trx 2 启动和提交的顺序如下:

  • trx 1 启动。
  • trx 2 启动。
  • trx 1 提交。
  • trx 2 提交。

其于以上假设,生成事务 ID 和事务提交号的流程如下:

  • trx 1 生成事务 ID,得到 100。trx_sys->next_trx_id_or_no 加 1,结果为 101。
  • trx 2 生成事务 ID,得到 101。trx_sys->next_trx_id_or_no 加 1,结果为 102。
  • trx 1 生成事务提交号,得到 102。trx_sys->next_trx_id_or_no 加 1,结果为 103。
  • trx 2 生成事务提交号,得到 103。trx_sys->next_trx_id_or_no 加 1,结果为 104。

从以上流程可以看到,事务 ID 和事务提交号都来源于 trx_sys->next_trx_id_or_no,相互之间不会重复。

2.3 修改 update undo 段状态

如果事务更新或删除了用户普通表的记录,InnoDB 会为事务分配一个 update undo 段。

如果事务更新或删除了用户临时表的记录,InnoDB 会为事务分配另一个 update undo 段。

InnoDB 可能会给事务分配 0 ~ 2 个 update undo 段。commit 子阶段会修改分配给事务的所有 update undo 段的状态。

如果 update undo 段满足缓存条件,它的状态会被修改为 TRX_UNDO_CACHED,否则,它的状态会被修改为 TRX_UNDO_TO_PURGE。

2.4 undo 日志组加入 history list

修改完 update undo 段的状态,update undo 段的 undo 日志组会加入回滚段的 history list 链表。purge 线程会从这个链表中获取要清理的 undo 日志组。

前面已经生成了事务提交号,这里会把事务提交号写入 undo 日志组的头信息中。

如果 update undo 段的状态为 TRX_UNDO_CACHED,表示这个 undo 段需要缓存起来。它会加入回滚段的 update_undo_cached 链表头部,以备后续其它事务需要 update undo 段时,能够快速分配。

3. InnoDB 提交事务完成

前面的一系列操作完成之后,InnoDB 提交事务的操作就完成了。

现在,要把事务状态修改为 TRX_STATE_COMMITTED_IN_MEMORY。

修改之后,新启动的事务就能看到该事务插入或更新的记录,看不到当前事务删除的记录。

接下来,InnoDB 会释放事务执行过程中加的表锁、记录锁。

释放锁之后,还要处理 insert undo 段。

如果 insert undo 段的状态为 TRX_UNDO_CACHED,表示这个 undo 段需要缓存起来。它会加入回滚段的 insert_undo_cached 链表头部,以备后续其它事物需要 insert undo 段时,能够快速分配。

如果 insert undo 段的状态为 TRX_UNDO_TO_FREE,它会被释放,占用的 undo 页会还给 undo 表空间。

二阶段提交的 flush 子阶段,已经把 prepare 阶段及之前产生的 redo 日志都刷盘了。

commit 子阶段,修改 insert undo 段和 update undo 段的状态,还会产生 redo 日志。

InnoDB 不会主动触发操作系统把这些 redo 日志刷盘,而是由操作系统决定什么时候把这些 redo 日志刷盘。

InnoDB 敢这么做,是因为这些 redo 日志对于确定事务状态已经不重要了。即使这些 redo 日志刷盘之前,服务器突然异常关机,导致 undo 段的状态丢失。MySQL 下次启动时,也能正确的识别到事务已经提交完成了。

4. 重新初始化事务对象

到这里,InnoDB 提交事务该做的操作都已经做完了。提交事务完成之后,该做的事也都做了。

对于上一个事务,事务对象的使命已经结束。这里会把事务状态修改为 TRX_STATE_NOT_STARTED。

事务对象也会被重新初始化,但是它不会被释放。也就是说,事务对象不会回到事务池中,而是留给当前连接后续启动新事务时复用。

5. 总结

InnoDB 提交事务,就像我们填完一个表格之后,最后盖上的那个戳,总体上来说,要干 3 件事。

第 1 件,修改分配给事务的各 undo 段的状态。

如果数据库发生崩溃,重新启动后,undo 段的状态是影响事务提交还是回滚的因素之一。

第 2 件,修改事务对象的状态。

如果数据据库一直运行,不发生崩溃,就靠事务对象的状态来标识事务是否已提交。

第 3 件,把各 undo 段中的 undo 日志组加入 history list 链表。

其它事务都不再需要使用这些 undo 日志时,后台 purge 线程会清理这些 undo 日志组中的日志。

原文地址:https://mp.weixin.qq.com/s/4IHIUbPMwB81m3JIhYwkXg

延伸 · 阅读

精彩推荐
  • MysqlPureFTP借助MySQL实现用户身份验证的操作教程

    PureFTP借助MySQL实现用户身份验证的操作教程

    这篇文章主要介绍了PureFTP借助MySQL实现用户身份验证的操作教程,就像普通程序中的用户注册功能那样为用户登陆数据信息建立一个数据库来进行验证,需要...

    MYSQL教程网4272020-05-28
  • MysqlMySQL8.0数据库开窗函数

    MySQL8.0数据库开窗函数

    数据库开窗函数是一种在SQL中使用的函数,它可以用来对结果集中的数据进行分组和排序,以便更好地分析和处理数据。开窗函数与聚合函数不同,它不会...

    你才是臭弟弟11012023-10-16
  • MysqlMySQL 视图 第1349号错误解决方法

    MySQL 视图 第1349号错误解决方法

    把下面SQL里的SELECT单独执行,没有问题,但是用来CREATE VIEW 就报错了. ...

    mysql教程网3462019-10-20
  • MysqlMySQL中查询所有数据库占用磁盘空间大小和单个库中所有表的大小的sql语句

    MySQL中查询所有数据库占用磁盘空间大小和单个库中所有表的大

    这篇文章主要介绍了在mysql中如何查询所有数据库占用磁盘空间大小的SQL语句,这样方便我们了解数据库的一些情况 ...

    MYSQL教程网3182020-01-09
  • MysqlMYSQL导入导出命令详解

    MYSQL导入导出命令详解

    网上看了一些总结出来的资料,不知道对大家有没有用的,有用的话也就不枉费我一按按钮的一片苦心了 ...

    mysql技术网5662019-10-18
  • Mysql详解Mysql如何实现数据同步到Elasticsearch

    详解Mysql如何实现数据同步到Elasticsearch

    要通过Elasticsearch实现数据检索,首先要将Mysql中的数据导入Elasticsearch,并实现数据源与Elasticsearch数据同步,这里使用的数据源是Mysql数据库。目前Mysql与...

    autofelix9952021-12-08
  • MysqlMYSQL 5.6 从库复制的部署和监控的实现

    MYSQL 5.6 从库复制的部署和监控的实现

    这篇文章主要介绍了MYSQL 5.6 从库复制的部署和监控的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋...

    明哥的运维笔记4292020-12-19
  • Mysql简单谈谈MySQL优化利器-慢查询

    简单谈谈MySQL优化利器-慢查询

    分析MySQL语句查询性能的方法除了使用 EXPLAIN 输出执行计划,还可以让MySQL记录下查询超过指定时间的语句,我们将超过指定时间的SQL语句查询称为“慢查询...

    狼骑舞者4582020-07-09