innodb_buffer_pool_wait_free > 0 表明 page cleaner 跟不上脏页生成,buffer pool 无空闲页,读请求排队、延迟陡增且易触发 oom;主因是长事务未提交导致 undo 和脏页锁住内存,需查 innodb_trx 定位超时大事务并限流干预,而非仅调小 buffer_pool_size。

innodb_buffer_pool_wait_free > 0 就说明 Buffer Pool 卡住了
这不是配置值写高了的“慢病”,而是实时内存调度已失效的“急诊信号”。Innodb_buffer_pool_wait_free 在 SHOW ENGINE INNODB STATUS\G 中非零,代表 page cleaner 线程跟不上脏页生成速度,buffer pool 里没空闲 chunk 可用,新读入页必须等淘汰——此时所有读请求都会排队,响应延迟陡增,且极易触发 OOM Killer。
常见诱因不是大查询本身,而是它背后的大事务:
- 长事务未提交,导致 undo log 持久驻留 buffer pool,且相关数据页无法被 LRU 驱逐(需保留一致性视图)
- 大事务执行期间持续修改同一张表,产生大量脏页,但刷盘节奏被
innodb_io_capacity和innodb_max_dirty_pages_pct压制,最终淤积 - 显式开启
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE后做范围更新,锁升级+间隙锁膨胀,进一步锁住更多页结构内存
怎么快速定位正在吃内存的大事务
别只看 SHOW PROCESSLIST 里状态为 Query 的线程——很多内存大户正安静地卡在 Sleep 或 Locked 状态,但事务没关。
执行这条语句直接揪出内存占用前三的活跃事务:
SELECT trx_id, trx_state, trx_started,
TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) AS duration_sec,
trx_mysql_thread_id,
SUBSTRING_INDEX(trx_query, ' ', 5) AS query_head
FROM information_schema.INNODB_TRX
ORDER BY duration_sec DESC
LIMIT 3;
重点关注 duration_sec > 300(5分钟以上)且 trx_state = 'RUNNING' 或 'LOCK WAIT' 的记录。如果 trx_query 是 UPDATE ... WHERE created_at > '2025-01-01' 这类全表扫描更新,基本就是罪魁祸首。
补充验证:查 information_schema.INNODB_LOCK_WAITS 确认是否有锁等待链;再用 SELECT * FROM performance_schema.events_statements_history_long WHERE thread_id = ? ORDER BY event_id DESC LIMIT 10(替换 ? 为上面查出的 trx_mysql_thread_id)回溯该线程最近执行过的语句。
大事务导致的 Buffer Pool 溢出,不能只 kill 连接
KILL 掉线程只是中断当前操作,但事务回滚阶段反而会更猛吃内存:undo log 要重放、脏页要清理、锁要释放——这个过程可能比运行时还耗资源,尤其当事务已修改数百万行时。
稳妥做法分两步走:
- 先设限:动态降低该连接的缓冲上限,减少后续消耗:
SET SESSION sort_buffer_size = 262144、SET SESSION read_buffer_size = 131072(单位字节),再观察Innodb_buffer_pool_wait_free是否回落 - 再干预:若事务已无业务意义,用
KILL QUERY(不是KILL)中止当前语句,保留事务上下文,避免强制回滚;若必须终止整个事务,确保 MySQL 正在低峰期且监控到Innodb_buffer_pool_pages_free> 5000 再执行KILL
永久规避:应用层加事务超时控制(如 Spring 的 @Transactional(timeout = 30)),数据库侧启用 innodb_rollback_on_timeout=ON(MySQL 5.7+ 默认关闭,需手动开)。
为什么调小 innodb_buffer_pool_size 有时没用
因为大事务撑起的内存不是 buffer pool “分配出去”的那部分,而是它“锁住不让释放”的那部分。即使你把 innodb_buffer_pool_size 从 12G 调到 8G,只要那个跑了 20 分钟的 UPDATE 还没提交,buffer pool 里就有至少 3–4G 是它死占着不放的脏页和 undo 页。
真正有效的缓解动作是:
- 立刻查
INNODB_TRX并人工介入,而不是等自动超时 - 检查
innodb_max_dirty_pages_pct是否设得过高(默认 75),生产建议压到 50–60,让刷脏更积极 - 确认
innodb_log_file_size是否足够支撑大事务——太小会导致频繁 checkpoint,加剧 buffer pool 压力;计算公式:日均 DML 行数 × 平均每行修改字节数 ÷ 3600 ÷ 0.7,结果向上取整到 GB 级
Buffer Pool 溢出的根子常不在池子大小,而在事务生命周期管理失控。盯着 trx_started 比盯着 innodb_buffer_pool_size 更早发现问题。











