未完成事务不直接占用buffer pool内存,而是通过阻止其修改的脏页被刷盘和淘汰,间接导致buffer pool淤积;需结合innodb_trx、innodb_buffer_pool_pages_dirty、innodb_lock_waits及show engine innodb status综合定位。

未完成事务本身不直接“占用”Buffer Pool内存
Buffer Pool 缓存的是数据页和索引页,不是事务对象。事务的锁、状态、undo日志等由独立子系统管理(lock_sys、trx_sys),它们不计入 innodb_buffer_pool_pages_total 或相关统计。所谓“事务占用Buffer Pool”,实际是指:该事务修改过的页仍留在Buffer Pool中、未被刷盘,且因事务未提交/回滚而无法被LRU淘汰或flush。
查哪些页被未提交事务长期持有
核心思路是交叉定位:先找出活跃事务,再确认其修改的页是否大量滞留于Buffer Pool。关键操作如下:
- 运行
SHOW ENGINE INNODB STATUS\G,重点看TRANSACTIONS部分,筛选出TRX_STATE = RUNNING且TRX_ROWS_MODIFIED > 0的事务,记下trx_id和trx_mysql_thread_id - 查该线程最近执行的SQL(
SELECT * FROM information_schema.PROCESSLIST WHERE ID = ?),确认是否在sleep、等待锁或执行大更新 - 检查脏页堆积:
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_pages_dirty',若数值持续高于正常波动范围(例如 > 总页数的10%),且与活跃事务数量正相关,说明有事务拖长了刷脏周期 - 观察
Pages made young和Pages flushed是否明显偏低——这是LRU链表老化和刷盘受阻的信号,常见于长事务阻塞flush_list
MySQL 5.7不支持直接按事务ID关联Buffer Pool页
5.7 版本没有 performance_schema.data_locks(需 8.0+)或 innodb_buffer_stats_by_table 中的事务上下文字段。你无法用一条SQL查出“trx_id=12345 修改了哪些Buffer Pool页”。替代方案只有间接推断:
- 用
SELECT * FROM information_schema.INNODB_TRX获取事务开始时间、运行时长、修改行数,结合业务逻辑判断是否异常 - 用
SELECT * FROM information_schema.INNODB_LOCK_WAITS确认是否存在锁等待链,等待方事务会间接延长持有方页的驻留时间 - 查
innodb_buffer_pool_pages_data与innodb_buffer_pool_pages_dirty的比值,若接近1(如 98% 以上),说明大量页被修改后未刷出,极可能由未提交事务导致
容易忽略的关键点
很多人盯着 innodb_buffer_pool_size 和 Pages free 看“空闲空间”,却漏掉一个事实:Buffer Pool里真正卡住系统性能的,往往不是“用了多少”,而是“哪些页该走没走”。一个持续30秒未提交的UPDATE,可能只改1行,但会让对应的数据页+undo页+索引页一直钉在Buffer Pool里,同时阻塞其他事务对同一页的访问和刷盘调度。这种影响无法从单个指标看出,必须把 INNODB_TRX、INNODB_LOCK_WAITS、Innodb_buffer_pool_pages_dirty 和 SHOW ENGINE INNODB STATUS 中的 LOG 段(看LSN差距)四者对照着看。











