lru淘汰发生在free list为空且需加载新页时,由页加载被动触发;新页默认插入lru中点(old sublist),仅二次访问且在innodb_old_blocks_time内才晋升至young sublist。

MySQL执行SQL时不会主动触发LRU链表淘汰,淘汰只发生在需要新页但Free List为空时,由页加载动作被动触发。
LRU淘汰何时真正发生?
淘汰不是SQL执行的“中间步骤”,而是内存分配失败后的兜底行为。当InnoDB要加载一个数据页(比如查询某行、更新索引项),先查Free List是否有空闲页;如果没有,才从LRU List尾部开始扫描,尝试淘汰冷页。
- 仅当
Free List长度为0时,才会进入LRU淘汰流程 - 全表扫描、大范围ORDER BY等操作可能批量加载页,快速耗尽Free List,从而高频触发淘汰
- 单条
SELECT或UPDATE通常不引发淘汰——只要Buffer Pool还有空闲页 - 后台线程(如
page_cleaner)会异步刷脏页,但不直接参与LRU淘汰决策
为什么新页不插到LRU头部?
为了避免预读页或临时查询页污染热区,InnoDB把新加载页默认插入LRU List的“中点”位置(即innodb_old_blocks_pct指定的分界处,默认37%,也就是约3/8处),属于Old SubList。
- 只有被再次访问(第二次命中)的Old页,才会被提升到Young SubList头部
- 这意味着一次性的全表扫描只会往Old区塞页,不会挤走正在被频繁访问的热点页
- 若误调
innodb_old_blocks_pct=0,相当于退化为传统LRU,极易导致缓存污染 -
innodb_old_blocks_time(单位毫秒)控制“二次访问窗口”:只有在该时间内再次访问,才触发晋升,否则仍留在Old区
哪些SQL操作最容易踩中LRU淘汰陷阱?
不是SQL本身“触发淘汰”,而是其访问模式让Buffer Pool不堪重负。以下场景需特别警惕:
- 未加LIMIT的
SELECT * FROM huge_table:预读机制可能一次加载数百页,瞬间抽干Free List - 缺失有效索引的
WHERE条件(如LIKE '%abc'):迫使InnoDB遍历大量数据页 - 批量INSERT/UPDATE未分批:事务内修改过多页,产生大量脏页并阻塞Free List回收(因脏页需先刷盘才能复用)
- 使用
SQL_NO_CACHE(已废弃)或强制绕过查询缓存,但对Buffer Pool无影响——它只作用于Server层旧Query Cache
如何确认当前LRU淘汰是否成为瓶颈?
关键指标不在慢日志,而在SHOW ENGINE INNODB STATUS输出的BUFFER POOL AND MEMORY段:
- 关注
Free buffers值持续为0,说明Free List长期枯竭 -
Database pages与Free buffers之和应≈total memory allocated(扣除控制块后) - 若
Pages made young远低于Pages read,说明大部分新页没被二次访问,Old区积压严重 - 配合
innodb_buffer_pool_read_requests和innodb_buffer_pool_reads看命中率:(read_requests - reads) / read_requests低于95%就值得排查
真正难处理的,是那些既不报错、也不慢,但悄悄把热点页挤出Young SubList的“温水煮青蛙”式查询——它们不会出现在慢日志里,却让核心接口缓存命中率逐日下滑。











