innodb_buffer_pool_wait_free持续非零即表明正发生buffer pool内存分配阻塞;需结合pages_free、performance_schema及sys.schema_table_statistics_with_buffer定位争用源,并检查buffer_pool_instances配置是否合理。

查innodb_buffer_pool_wait_free是否持续非零
这个状态值是 buffer pool 竞争最直接的信号:只要它大于 0,就说明有线程在等空闲页,底层已出现内存分配阻塞。不是“可能有竞争”,而是“正在发生竞争”。
执行 SHOW STATUS LIKE 'innodb_buffer_pool_wait_free';,观察返回值。生产环境里,这个值应该长期为 0;如果每秒都刷出 1~5,甚至更高,基本可以断定 buffer pool 太小或查询太暴力。
- 别只看单次快照——用
watch -n 1 'mysql -e "SHOW STATUS LIKE \"innodb_buffer_pool_wait_free\";"'持续盯 30 秒以上 - 该指标和
innodb_buffer_pool_pages_free要一起看:后者为 0 + 前者 > 0 = 确诊重度争用 - 注意它不区分“谁在等”,只反映全局压力;要定位到具体 SQL,得往下走
用performance_schema.events_statements_history_long抓高延迟读操作
buffer pool 竞争常表现为单条 SQL 的 fetch_latency 异常飙升,尤其是 SELECT 中带大范围扫描、JOIN 或未命中索引的场景。这类语句会批量申请页、触发预读、挤占 free list,是真正的“热点制造机”。
开启相关 consumer(如未开):UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_statements_history_long', 'events_waits_history_long');
- 查最近 10 条 fetch 耗时最高的语句:
SELECT DIGEST_TEXT, TIMER_WAIT, LOCK_TIME, ROWS_EXAMINED FROM performance_schema.events_statements_history_long WHERE EVENT_NAME LIKE 'statement/sql/select' ORDER BY TIMER_WAIT DESC LIMIT 10; - 重点筛
ROWS_EXAMINED远大于ROWS_SENT的语句——说明扫了大量页但只返回几行,极可能是锁等待或全表扫描拖慢了 buffer pool 页面流转 - 如果
TIMER_WAIT高但LOCK_TIME很低,大概率是 buffer pool 内部争用(比如 LRU list 锁、chunk 分配锁),而非事务锁
关联sys.schema_table_statistics_with_buffer找高fetch_latency表
这张视图把表级 I/O 延迟和 buffer pool 行为绑在一起,比单纯看 rows_read 有用得多。真正卡住 buffer pool 的,往往不是读得多的表,而是“每次读都特别慢”的表。
执行:SELECT table_schema, table_name, fetch_latency, io_latency, rows_read, (fetch_latency / rows_read) AS avg_fetch_us FROM sys.schema_table_statistics_with_buffer WHERE rows_read > 0 ORDER BY avg_fetch_us DESC LIMIT 5;
-
avg_fetch_us超过 10000(即 10ms/行)就要警惕;超过 50000 基本可判定该表访问路径严重受 buffer pool 竞争拖累 - 若
io_latency很低但fetch_latency很高,说明数据已在内存中,但取页过程被阻塞(例如 LRU 中段插入锁、free list 竞争) - 该视图默认不统计临时表,如果业务用了大量
CREATE TEMPORARY TABLE,这部分不会出现在结果里,需单独查information_schema.INNODB_TEMP_TABLE_INFO
确认是否innodb_buffer_pool_instances配置失当
实例数太少,会导致所有线程抢同一把 LRU list 锁或 free list 锁,尤其在高并发读场景下,innodb_buffer_pool_wait_free 就会跳高。这不是 buffer pool 太小,而是“分柜子”没分好。
检查当前配置:SELECT @@innodb_buffer_pool_instances, @@innodb_buffer_pool_size / 1024 / 1024 / 1024 AS size_gb;
- 32G+ buffer pool 建议设为 8 或 16;16G 及以下设 4 或 8 即可;别设成 1(等于没分)
- 调整时必须同步满足 chunk 对齐:新
innodb_buffer_pool_size必须是innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的整数倍(默认 chunk 是 128M) - 改完立刻查
SHOW ENGINE INNODB STATUS\G里的 “Mutex spin waits” —— 如果 “buf_pool_mutex” 或 “buf_pool_LRU_list_mutex” 相关计数异常高,就是实例数不够的铁证
真实线上 case 里,最常被忽略的是:buffer pool 竞争不一定来自“大查询”,而可能来自几十个并发执行的中等复杂度 SELECT,它们集体在 LRU 中段插入、反复触发 page clean、又同时抢 free list。这时候看单条 SQL 没明显问题,但整体 fetch_latency 和 wait_free 已经拉响警报。











