是二级索引在拖累buffer pool:低选择性或宽二级索引导致索引页大量涌入却低频访问,引发回表污染、youngs/s偏低、命中率卡在700–850,需结合索引使用率、覆盖度和innodb_old_blocks_pct协同优化。

查清是不是二级索引真在拖累Buffer Pool
二级索引本身不直接“过载”,但大量低选择性、未覆盖查询或宽索引会引发连锁反应:索引页频繁被读入Buffer Pool,却只用其中几行;同时主键回表又拉取聚簇索引页,导致Buffer Pool里塞满碎片化、访问频次极低的页。典型表现是:Innodb_buffer_pool_read_requests很高,但youngs/s很低(SHOW ENGINE INNODB STATUS\G中 BUFFER POOL AND MEMORY 段),data_pages / total_pages接近 1.0,而命中率卡在 700–850 之间。
先确认问题源头:
- 查
performance_schema.table_io_waits_summary_by_table,看哪些表的COUNT_READ异常高,再结合information_schema.STATISTICS比对这些表是否建了多个宽二级索引(比如INDEX (a,b,c,d,e)) - 用
EXPLAIN FORMAT=JSON跑慢查询,关注used_columns和key_parts字段——如果实际只用前两列,却走了五列索引,说明索引设计冗余 - 检查
Innodb_buffer_pool_pages_misc是否持续 > 10%,这可能是自适应哈希索引(AHI)因二级索引分裂频繁重建导致的内存占用激增
删掉或收缩低效二级索引
别迷信“有索引总比没有好”。二级索引页和聚簇索引页一样占Buffer Pool空间,且更新时还要维护额外的写放大。真正该留下的索引必须满足两个条件:被高频查询命中 + 覆盖足够多字段。
操作建议:
- 用
sys.schema_unused_indexes(MySQL 5.7+)或自建脚本统计information_schema.INDEX_STATISTICS(需开启userstat),筛出90天内SELECTS = 0的索引,直接DROP INDEX - 对宽索引做裁剪:比如现有
INDEX (status, created_at, user_id, order_id),但业务95%查询只过滤status和created_at,那就建INDEX (status, created_at),删掉旧索引 - 避免在
VARCHAR(255)字段上建全文索引以外的二级索引——除非加了前缀长度(如VARCHAR(255)→INDEX (name(10))),否则单页存不了几条记录,Buffer Pool利用率极差
用覆盖索引减少回表带来的Buffer Pool污染
回表是二级索引导致Buffer Pool命中率下降的最直接推手:一次查询先读二级索引页(可能命中),再根据主键去聚簇索引里捞数据(大概率不命中,触发磁盘读)。覆盖索引把所有需要字段都塞进索引页,一步到位。
实操要点:
- 覆盖索引必须包含
WHERE、ORDER BY、GROUP BY和SELECT里所有列;SELECT *永远无法被覆盖,必须显式列出字段 - 注意
NULL字段处理:如果索引列允许NULL,InnoDB会在索引页额外存一个NULL bitmap,增大页体积——能NOT NULL就别留空 - 联合索引顺序不能错:比如查询
WHERE a = ? AND b > ? ORDER BY c,索引应为(a, b, c),而非(a, c, b);否则c无法用于排序,仍要回表排序
调innodb_old_blocks_pct给二级索引页“冷处理”
默认innodb_old_blocks_pct = 37对OLTP点查友好,但对二级索引扫描类查询(如SELECT ... WHERE status IN ('paid','shipped'))就是灾难:几百个索引页一股脑涌进Old区,还没来得及被二次访问就被淘汰,顺带挤出热数据。
调整策略取决于负载类型:
- 纯OLTP(ID点查为主):保持默认
37,改小反而让预读页过早升Young,污染加剧 - 混合负载(白天点查 + 夜间报表扫描二级索引):设为
25~30,缩小白名单式污染范围 - BI直连分析型查询(大量
WHERE走二级索引 +GROUP BY):压到15~20,但必须同步调高innodb_old_blocks_time到2000~3000,否则扫描页刚进Old区就被踢 - 绝对不要设成
5或95——前者让二级索引页根本进不了LRU链,后者等于放弃分代保护,热数据秒丢
执行命令:SET GLOBAL innodb_old_blocks_pct = 25;(动态生效,无需重启)
真正难的是判断二级索引页到底算“冷”还是“热”:它可能对某类查询是热的,对另一类却是冷的。没统一解法,得盯着youngs/s和non-youngs/s比值调,比值低于 0.1 就说明老区页基本没晋升机会,该动参数了。











