innodb使用lru算法管理缓冲池,新页插入midpoint(默认5/8处)分young/old区,通过innodb_old_blocks_pct和innodb_old_blocks_time协同控制冷页晋升,避免全表扫描冲击热点数据。

内存不足时频繁刷盘,根本原因不是脏页太多,而是LRU淘汰触发了同步刷脏——当查询需要加载新页、而LRU尾部全是脏页时,InnoDB必须先刷盘再释放页,导致查询线程卡住等I/O。
为什么查个表就触发大量flush
这不是配置错了,是InnoDB的LRU淘汰机制在“被迫干活”:它只从old sublist尾部淘汰页,但如果那里全是脏页,就得同步刷盘才能腾出干净页。尤其在以下场景中极易发生:
- 执行过全表扫描(
SELECT * FROM huge_table),把大量冷页塞进old区,又没被再次访问,长期滞留 -
innodb_old_blocks_time设得太低(比如 0 或 100),导致冷页刚进来就被访问一次就晋升到young区,挤占空间后又很快变冷,反复横跳 -
innodb_old_blocks_pct设得太高(比如 >50),old区过大,冷页堆积更多,尾部脏页密度上升 - 缓冲池整体偏小(
innodb_buffer_pool_size低于物理内存50%),LRU链表太短,冷热混杂更严重
怎么调innodb_old_blocks_pct和innodb_old_blocks_time
这两个参数要一起看,目标是让冷页“快进快出”,别赖在old区拖慢淘汰速度:
-
innodb_old_blocks_pct建议调到25(不能低于5),缩小old区,让冷页更快走到尾部被淘汰 -
innodb_old_blocks_time建议设为2000(2秒)或更高,避免扫描中偶然访问就晋升;SSD延迟低可设1500,HDD建议3000 - 改完后观察
SHOW ENGINE INNODB STATUS\G中的pages made young和pages not made young比例,理想是后者明显多于前者(说明冷页没乱晋升)
innodb_lru_scan_depth设高还是设低
这个值控制每次LRU淘汰时往尾部扫多少页找可淘汰对象,不是越大越好:
- 设太高(比如 >2048)会让InnoDB每次扫太深,CPU开销上升,尤其在buffer pool大(>32GB)、实例负载高时,可能引发周期性抖动
- 设太低(比如
- 生产环境推荐值:
1024(默认值),若buffer pool ≤16GB可保持默认;≥32GB且实例为SSD+高并发,可试2048,但必须配合监控Innodb_buffer_pool_wait_free是否上升
真正该优先检查的其实是free list是否枯竭
LRU淘汰只是后备手段,InnoDB第一反应是找free list里的空闲页。如果free list长期为0,说明预分配/复用机制已失效:
- 查
SHOW ENGINE INNODB STATUS\G中Free buffers值,持续 ≤10 就危险 - 确认是否开了
innodb_buffer_pool_instances且设置合理(≥8 for ≥32GB),否则单实例锁争抢会卡住free list维护 - 检查是否有大事务长期持有页未释放(
TRX_RUNNING_FOR超长),或change buffer积压未合并,它们都占用free list配额
调参只是止痛,真正稳住内存不抖,得让冷页不赖着、free list不断供、脏页刷得平滑——三者缺一不可。尤其是innodb_old_blocks_pct和innodb_old_blocks_time的组合,稍一失衡,LRU就从保护机制变成性能放大器。











