【Mysql 数据库 History list length 超长, 且undo log 文件无限膨胀,性能下降等问题解决方案】

1. 问题描述

在 Mysql 数据目录中发现有个 undo 文件非常大,并且持续增长
在这里插入图片描述

  • 并且 History list length 非常大
------------
TRANSACTIONS
------------
Trx id counter 3569860310
Purge done for trx's n:o < 3185146100 undo n:o < 0 state: running but idle
History list length 194564756
LIST OF TRANSACTIONS FOR EACH SESSION:
  • 并且可能会发现同时如下问题:
    • Mysql 的性能在下降,TPS 并不高的情况下 IO wait 非常高,服务器负载也上升。
    • 如果是从库,那么主从复制延迟也会增加。

2. 排查过程

  • 背景知识: History list length是指在回滚空间中的未清除事务数。随着事务的提交,它的值会增加;随着清除线程的运行,它的值会减小。

  • 实例: 发现 主库的 History list length 值正常,从库的 History list length 非常大

    • 此时可能有如下问题表现:

      1. 主从复制延迟增大
      2. 数据库数据读写慢,性能降低, iowait 较高。
      3. 从库的 undolog 无限膨胀, 或者 innodb_undo_log_truncate 机制未生效,无法收缩 undolog 文件的大小。
      4. 由于较长的 InnoDB 历史记录列表会减慢数据库关闭速度,因此在启动涉及数据库关闭的操作之前,请减小列表大小
    • 查看发现未提交的事物,可见这个事物长达几个月没有结束。

      mysql> SELECT * FROM information_schema.INNODB_TRX\G
      *************************** 1. row ***************************
                          trx_id: 421966903910096
                       trx_state: RUNNING
                     trx_started: 2024-06-13 19:11:42
           trx_requested_lock_id: NULL
                trx_wait_started: NULL
                      trx_weight: 0
             trx_mysql_thread_id: 247570
                       trx_query: select count(*) from t_xxx_user
             trx_operation_state: counting records
               trx_tables_in_use: 1
               trx_tables_locked: 0
                trx_lock_structs: 0
           trx_lock_memory_bytes: 1136
                 trx_rows_locked: 0
               trx_rows_modified: 0
         trx_concurrency_tickets: 0
             trx_isolation_level: REPEATABLE READ
               trx_unique_checks: 1
          trx_foreign_key_checks: 1
      trx_last_foreign_key_error: NULL
       trx_adaptive_hash_latched: 0
       trx_adaptive_hash_timeout: 0
                trx_is_read_only: 1
      trx_autocommit_non_locking: 1
      1 row in set (0.00 sec)
      
  • 原因可能:

    • 复制延迟会导致从库的 History list length 增加,因为未处理的事务会保留在undo日志中。

    • 长查询和事务锁会导致 History list length 增加,因为相关的undo日志无法被及时清理。

    • Mysql 写入负载太重

    • 失败的事务
      mysql中使用的隔离级别是默认的:REPEATABLE-READ。在这个级别下,保证了多次读的结果一样(跟MVCC有关)。而这个业务对于失败的事务不做任何操作,导致可能存在这个事务一直没commit,为了保持隔离级别REPEATABLE-READ 的特性,在undo中一直保留了这个事务以后的版本号,也就导致未清理的事务数越来越大,History list length 值也就越大。

      InnoDB 实现了多版本并发控制 (MVCC),这意味着不同的用户将看到他们正在交互的数据的不同版本(有时称为快照,这是一个有点误导性的术语)。这样做是为了允许用户看到系统的一致视图,而不会出现成本高昂且影响性能的锁定,这会限制并发性。(这就是术语的“并发控制”部分的来源;一种替代方法是锁定用户可能需要的所有内容。撤消日志记录和 InnoDB 的“历史”系统是其实现 MVCC 的基础机制

      • InnoDB 中的挂起事务将导致 InnoDB 历史长度不受控制地增长,这将直接对 MySQL 中的 SELECT 查询性能产生负面影响。我们已经通过将 TRANSACTION ISOLATION LEVEL 更改为 READ-COMMITTED 为多个客户解决了这个问题。从技术上讲, InnoDB History Length 是撤消日志,可用于为 MVCC 目的重新创建历史记录,也可以回滚。
  • 解释 HLL 值

     解释 HLL 值时,请考虑下表中列出的准则:
     值  备注
     小于 ~10,000  正常值,表明垃圾回收未滞后。
     介于 ~10,000 和 ~1,000,000 之间 这些值表明垃圾回收有轻微的滞后。 如果此类值保持稳定且不会增加,则这些值可能是可接受的。
     大于 ~1,000,000  应调查这些值,可能需要纠正措施
    

3. 解决方案

  • 解决方案:

    • 识别并结束长时间运行的事务
      SELECT * FROM information_schema.INNODB_TRX\G

      • 注意: kill掉失败的事物所处的线程,若 kill后依然出现在 processList 中,那么只能重启数据库试试。
    • 使用READ-COMMITTED 而不是默认级别 REPEATABLE-READ

      • 如果是读写分离的主从架构,建议主库使用 REPEATABLE-READ 而从库使用 READ-COMMITTED

      • 修改方案

        • 如果 undolog 非常大,建议先检查 innodb_undo_log_truncate 是否开启。
          show variables like '%undo%';
          
        • 然后检查隔离级别
          mysql> SHOW VARIABLES LIKE 'transaction_isolation';
          +-----------------------+-----------------+
          | Variable_name         | Value           |
          +-----------------------+-----------------+
          | transaction_isolation | REPEATABLE-READ |
          +-----------------------+-----------------+
          1 row in set (0.00 sec)
          
        • 临时修改隔离级别
          SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
          
          • 注意,这个设置是全局的,并且会影响从库上的所有新会话。现有的会话可能会继续使用它们自己的隔离级别设置,直到它们被关闭并重新打开。
        • 永久修改 : 修改 my.cnf 文件,然后重启 Mysql
          [mysqld]
          transaction_isolation=READ-COMMITTED
          
          • 重启:
          # 从库建议先主动停止复制,然后重启
          stop slave;
          sudo systemctl restart mysqld
          
        • 检查效果:
          # 查看 History list length 是否逐渐减少
          SHOW ENGINE INNODB STATUS;
          
  • 如何避免?

    • 避免长事物! 如果无法避免,建议针对长事物单独使用 READ-COMMITTED

      • 以 SpringBoot 为例: @Transactional(isolation = Isolation.READ_COMMITTED)
    • 使用READ-COMMITTED 而不是默认级别 REPEATABLE-READ

    • 如果是读写分离的主从架构,建议主库使用 REPEATABLE-READ 而从库使用 READ-COMMITTED

      • 主库用 REPEATABLE-READ(可重复读)
        • 优势:
          • 确保同一事务的多次读取操作会看到相同的数据。
          • 解决了脏读(读取未提交数据)和不可重复读(同一事务中多次读取结果不一致)的问题。
          • 但可能存在幻读(一个事务读取某个范围的数据行时,另一个事务在该范围内插入了新行,导致前一个事务再次读取时出现“幻影”行)的问题。不过,MySQL 的 InnoDB 存储引擎通过多版本并发控制(MVCC)机制缓解了幻读问题。
        • 使用场景:
          • 确保数据一致性的写操作频繁的场景。
          • 主库上,由于涉及到数据的更新和删除,使用 REPEATABLE-READ 可以确保事务的原子性和一致性。
      • READ-COMMITTED(读已提交)
        • 特点:
          • 一个事务只能看到已经提交事务所做的更改。
          • 解决了脏读问题,但可能出现不可重复读和幻读。
          • 提高了并发性能,因为读取操作不需要等待其他事务的提交。
        • 适用场景:
          • 读操作频繁,且对一致性要求不高的场景。
          • 从库上,由于主要用于读取数据,使用 READ-COMMITTED 可以提高并发性能,减少锁争用。
  • 其他参考: (https://minervadb.xyz/troubleshooting-innodb-history-length-with-hung-mysql-transaction/)
    在这里插入图片描述

  • Mysql官网建议 :(https://dev.mysql.com/blog-archive/performance-impact-of-innodb-transaction-isolation-modes-in-mysql-5-7/)
    在这里插入图片描述

    • 更多参考:
      • https://docs.aws.amazon.com/en_us/AmazonRDS/latest/AuroraUserGuide/proactive-insights.history-list.html
      • https://minervadb.xyz/troubleshooting-innodb-history-length-with-hung-mysql-transaction/
      • https://blog.jcole.us/2014/04/16/the-basics-of-the-innodb-undo-logging-and-history-system/
      • https://dev.mysql.com/blog-archive/performance-impact-of-innodb-transaction-isolation-modes-in-mysql-5-7/
<think>好的,我现在需要回答用户关于MySQL事务表中的记录何时消失的问题。用户之前已经询问过关于大事务表查询长时间显示运行的问题,现在他想了解事务表记录的消失时机。我需要确保回答准确,并覆盖不同的情况和机制。 首先,我需要回顾MySQL中事务处理的基本概念,特别是InnoDB存储引擎,因为事务处理主要与InnoDB相关。InnoDB使用undo日志来处理事务的回滚和多版本并发控制(MVCC)。当事务提交或回滚后,相关的undo日志可能不会立即删除,因为它们可能还被其他活动事务需要用于MVCC或回滚。 接下来,要考虑事务记录的消失时间点。这涉及到几个方面:事务提交后的处理、undo日志的清理、以及purge线程的作用。此外,长时间运行的事务可能会影响undo日志的保留时间,因为如果有一个旧的事务仍然在运行,InnoDB需要保留该事务开始之后的所有undo日志,以确保能够构造一致性的读视图。 另外,用户提到的“事务表的记录”可能指的是事务在系统表或信息模式中的记录,比如INNODB_TRX表中的信息。这些记录在事务结束后会被清除,但具体时间可能依赖于MySQL的内部机制和配置参数。 还需要考虑自动提交设置的影响。在自动提交模式下,每个SQL语句都是一个独立的事务,执行后立即提交,这样相关的事务记录可能很快被清理。而对于显式事务(BEGIN...COMMIT/ROLLBACK),事务结束后记录才会被处理。 此外,purge线程的作用非常重要。InnoDB的后台线程负责清理不再需要的undo日志和旧的行版本。如果purge线程滞后,可能会导致事务记录保留时间较长。配置参数如innodb_purge_threads和innodb_max_purge_lag会影响purge的效率。 还要注意长时间未提交的事务可能导致undo日志不断增长,占用大量磁盘空间,甚至导致性能问题。因此,监控和管理长时间运行的事务是必要的。 总结可能的情况:事务提交或回滚后,undo日志由purge线程逐步清理,具体时间取决于系统负载、配置和是否有活动事务依赖这些日志。INNODB_TRX表中的记录在事务结束后立即消失,而undo日志的物理删除则需要等待purge操作完成。 需要验证这些点,并确保回答清晰,涵盖不同情况,如事务提交、回滚、自动提交、显式事务、purge线程的影响,以及相关配置参数的作用。此外,提供检查和监控的方法,如查询INNODB_TRX和INNODB_UNDO_LOGS表,查看purge状态等,可以帮助用户实际应用这些信息。</think>在 MySQL 中,事务表的记录(如 InnoDB 的事务日志、undo log 等)的“消失”时间取决于事务的状态、隔离级别、存储引擎机制以及系统配置。以下是关键场景和原理: --- ### **1. 事务提交后的记录清理** - **InnoDB 的 MVCC 机制**: 当事务提交后,其修改的数据行并不会立即被物理删除,而是通过 **undo log** 保留旧版本,以支持其他事务的 **一致性读(MVCC)**。 - 当没有其他事务需要这些旧版本时(即所有活动事务的视图不再依赖这些旧数据),InnoDB 的后台线程(purge thread)会逐步清理对应的 undo log。 - 清理速度受参数 `innodb_purge_threads`(控制 purge 线程数)和 `innodb_max_purge_lag`(限制未清理的 undo log 量)影响。 --- ### **2. 事务回滚时的记录处理** - **显式回滚(`ROLLBACK`)**: 事务内的修改会直接撤销,undo log 被标记为可回收,由 purge 线程异步清理。 - **隐式回滚**: 如果事务因连接断开或超时被自动回滚,undo log 同样会被标记为待清理。 --- ### **3. 事务表的元数据记录** - **`INFORMATION_SCHEMA.INNODB_TRX` 表**: 存储当前活跃事务的信息。当事务提交或回滚后,对应记录会立即从该表中移除。 ```sql SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX; -- 仅显示未结束的事务 ``` --- ### **4. 物理存储中的行记录** - **删除操作(`DELETE`)**: 即使执行了 `DELETE`,数据行也不会立即从磁盘删除,而是标记为“删除待清理”(逻辑删除)。 - **物理删除时机**: - 当 purge 线程确定该行不再被任何事务需要时(如无活跃事务需要旧版本),才会真正从磁盘移除。 - 如果存在长事务(如未提交的事务或慢查询),可能导致旧版本长期残留。 --- ### **5. 事务日志的持久化** - **Redo Log(重做日志)**: 用于崩溃恢复,记录事务的物理修改。 - **清理时机**: - 当对应修改的数据页刷盘(checkpoint)后,旧的 redo log 会被覆盖复用。 - 由参数 `innodb_log_file_size` 和 `innodb_log_files_in_group` 控制日志文件大小和数量。 --- ### **6. 长事务对记录保留的影响** - **风险场景**: 如果一个事务长时间未提交(如忘记提交或代码逻辑缺陷),其关联的 undo log 和旧数据版本将无法被清理,导致: - 表空间膨胀(尤其是独立表空间 `innodb_file_per_table=ON`)。 - 磁盘空间持续增长(undo log 堆积)。 - **监控方法**: ```sql -- 查看长事务(重点关注 Trx_started 时间) SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX ORDER BY Trx_started ASC LIMIT 10; ``` --- ### **7. 手动触发清理** - **强制终止事务**: 使用 `KILL <thread_id>` 终止长事务后,其 undo log 会进入待清理队列。 - **优化表空间**: 执行 `OPTIMIZE TABLE <table_name>` 会重建表并释放未使用的空间,但需谨慎使用(锁表耗时)。 --- ### **总结:记录“消失”的关键条件** | **记录类型** | **消失条件** | |--|-----------------------------------------------------------------------------| | 事务元数据(INNODB_TRX) | 事务提交或回滚后立即移除。 | | Undo Log(旧版本数据) | 所有依赖该版本的事务结束后,由 purge 线程清理。 | | Redo Log(重做日志) | 数据页刷盘后,日志空间被循环复用。 | | 物理删除的行记录 | purge 线程确认无事务需要旧版本后删除。 | --- ### **排查建议** 1. **监控长事务**: ```sql -- 查看未提交事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX; ``` 2. **检查 undo log 状态**: ```sql SHOW VARIABLES LIKE 'innodb_undo%'; -- 查看 undo 表空间配置 ``` 3. **观察 purge 进度**: ```sql SHOW ENGINE INNODB STATUS\G; -- 在输出中搜索 "History list length" ``` - `History list length` 表示未清理的 undo log 数量,数值越大说明清理压力越大。 --- ### **优化配置** - 增加 purge 线程数: ```sql SET GLOBAL innodb_purge_threads = 4; -- 默认 4,可根据负载调整 ``` - 限制事务持续时间: ```sql SET GLOBAL innodb_rollback_on_timeout = ON; -- 超时自动回滚 SET GLOBAL wait_timeout = 600; -- 非交互连接超时时间(秒) ``` 合理管理事务提交和监控长事务,是避免记录残留的关键!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值