MySQL8.0 · 引擎分析 · InnoDB history list 无法降到0的原因

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 熟悉InnoDB的朋友都知道,innodb的history list长度代表了有多少undo日志还没有被清理掉,可以通过show engine innodb status 命令来获得。如果发现history list的长度越大,要么就是实例的复杂非常高,要么就是可能有大查询,或者事务没提交,导致Undo log无法分析。

熟悉InnoDB的朋友都知道,innodb的history list长度代表了有多少undo日志还没有被清理掉,可以通过show engine innodb status 命令来获得。如果发现history list的长度越大,要么就是实例的复杂非常高,要么就是可能有大查询,或者事务没提交,导致Undo log无法分析。

但如果仔细观察,大家是否发现,history list居然无法降到0,即使做一次slow shutdown也不行。因为理论上来说,如果undo日志都已经purge干净了,理论上应该能下降为0。

为了更好的理解,我们先普及几个概念。首先innodb支持多个rollback segment,每个segment包含约1024个slot。

当事务开启时,会给它指定使用哪个rollback segment,然后在真正执行操作时,分配具体的slot,通常会有两种slot:

  • update_undo: 只用于事务内的update语句
  • insert_undo:只用于事务内的insert语句

通常如果事务内只包含一种操作类型,则只使用一个slot。但也有例外,例如insert操作,如果insert的记录在page上已经存在了,但是是无效的,那么久可以直接通过更新这条无效记录的方式来实现插入,这时候使用的是update_undo.

为什么要分成两种undo slot,而不是只用一个slot处理所有呢?这是因为在提交阶段的undo处理不同:

对于Insert undo, 有两种处理方式

  • Free: 直接清理掉,因为我们知道新插入的记录产生的Undo不会被任何查询语句所引用,因此可以直接释放undo,这里的undo log不会累加到history list上
  • reuse: 当undo 只占用一个page,且page使用低于一定比例时(事实上,第二个条件对于insert undo可以移除掉),放到cachd list上,以备重用。 在重用时,会将该page reset掉

    对于update_undo: 也有两种处理方式:

  • Purge: 这里会加入到其对应rollback segment的history list数据页列表上,history list长度加1
  • Reuse: 同样会将undo加到history list上,history list长度加1。by the way, update undo和insert的重用方式不同,它会在undo page上新建一个undo log header, 而不是重置page。这意味着一个undo页上可能有多个undo log分属不同的事务,但只有一个可能是活跃的。

那么回到最初的问题,既然undo log都加到history list了,为啥在undo purge完成后,未重置为0呢?

我们来看看如下函数

    trx_purge_truncate
      trx_purge_truncate_history
                trx_purge_truncate_rseg_history

在函数trx_purge_truncate_rseg_history中,有如下代码段:

        if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
            && (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {

                /* We can free the whole log segment */

                mutex_exit(&(rseg->mutex));
                mtr_commit(&mtr);

                trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);

                n_removed_logs = 0;
        } else {
                mutex_exit(&(rseg->mutex));
                mtr_commit(&mtr);
        }

这里做了特殊判断,只有状态为PURGE的undo log才做了free segment清理。对于cached状态的undo留在原地。个人猜测是因为这些undo log可以留作重用, 在重用之后,再做一次性清理。

为了验证猜测,修改函数trx_undo_set_state_at_finish,使undo log状态,要么为TRX_UNDO_TO_FREE, 要么为TRX_UNDO_TO_PURGE。

在给实例加了一定的负载,再做一次slow shutdown重启后,history list length的长度果然变成了0。验证了其无法重置为0是由于cached undo导致。

相关实践学习
自建数据库迁移到云数据库
本场景将引导您将网站的自建数据库平滑迁移至云数据库RDS。通过使用RDS,您可以获得稳定、可靠和安全的企业级数据库服务,可以更加专注于发展核心业务,无需过多担心数据库的管理和维护。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
Apache 索引
精进Hudi系列|Apache Hudi索引实现分析(五)之基于List的IndexFileFilter
精进Hudi系列|Apache Hudi索引实现分析(五)之基于List的IndexFileFilter
213 0
|
存储 人工智能 关系型数据库
10个行锁、死锁案例⭐️24张加锁分析图🚀彻底搞懂Innodb行锁加锁规则!
10个行锁、死锁案例⭐️24张加锁分析图🚀彻底搞懂Innodb行锁加锁规则!
|
存储 SQL 关系型数据库
Mysql鸡础(从数据库中导入学生数据用list集合存储emp成员)
Mysql鸡础(从数据库中导入学生数据用list集合存储emp成员)
|
SQL NoSQL 关系型数据库
Mysql Innodb死锁情况分析与归纳
Mysql Innodb死锁情况分析与归纳
230 0
|
容器
List源码模拟与分析
List源码模拟与分析
125 0
|
JSON Java 应用服务中间件
TypeToken分析(json字符串- list对象)
TypeToken分析(json字符串- list对象)
402 0
|
关系型数据库 MySQL Windows
Mysql (ONLY_FULL_GROUP_BY) Expression #1 of SELECT list is not in GROUP BY ...
Mysql (ONLY_FULL_GROUP_BY) Expression #1 of SELECT list is not in GROUP BY ...
206 0
|
关系型数据库
InnoDB行级锁的分析
在主键上的查询 下面做过实验
169 0
|
SQL NoSQL 安全
Percona 8.0.30中"show engine innodb status"导致coredump排查及分析
Percona 8.0.30中"show engine innodb status"导致coredump排查及分析
347 0
|
SQL 关系型数据库 MySQL
解决Mysql5.7以上版本, 使用group by抛出Expression #1 of SELECT list is not in GROUP BY clause and contains no异常
解决Mysql5.7以上版本, 使用group by抛出Expression #1 of SELECT list is not in GROUP BY clause and contains no异常
313 0

相关产品

  • 云数据库 RDS MySQL 版