optimizer_switch中index_merge=on可能降低查询性能,因其强制合并单列索引导致多次回表和排序开销,应通过EXPLAIN识别Using union/intersect并改用复合索引。

为什么 optimizer_switch 里开 index_merge=on 反而让查询变慢
MySQL 默认启用 index_merge,但它不是“越多索引越好”的开关。当多个单列索引同时存在,优化器可能强行合并它们,生成 Using sort_union 或 Using intersect 计划,实际要回表多次、排序开销大,比走一个复合索引还慢。
- 先用
EXPLAIN看执行计划里是否出现Using union/Using intersect—— 这是信号 - 临时关闭:
SET SESSION optimizer_switch='index_merge=off'; - 长期建议:删掉冗余单列索引,替换成覆盖查询条件的复合索引,比如
(user_id, status, created_at) - 注意:5.7+ 中
index_merge对IN子查询支持有限,容易误判,关掉更稳
range_optimizer_max_mem_size 设太小会跳过有效索引
这个变量控制 MySQL 在评估 range 访问路径时,允许使用的内存上限(默认 16MB)。如果估算需要的内存超过它,优化器会直接放弃 range 扫描,退化成全表扫描或用其他低效方式 —— 即使你明明建了索引。
- 现象:
EXPLAIN显示type: ALL或type: index,但 where 条件明显能走索引 - 查当前值:
SELECT @@range_optimizer_max_mem_size;(单位字节) - 调大示例:
SET SESSION range_optimizer_max_mem_size = 67108864;(64MB) - 别无脑设到 2GB:内存超限会导致语句被拒绝,且只影响当前 session;全局修改需写入
my.cnf的[mysqld]段
用 FORCE INDEX 强制走索引,但 rows 显示 0 是怎么回事
FORCE INDEX 能绕过优化器误判,但 EXPLAIN 里 rows 为 0 表示统计信息严重失真,优化器无法估算扫描行数,可能触发错误计划,比如本该走二级索引却强制走主键。
- 根本原因:表没 ANALYZE,或
innodb_stats_persistent关闭 + 数据变动大 - 立刻修复:
ANALYZE TABLE t_name;(InnoDB 表推荐用此命令而非OPTIMIZE) - 长期设置:
SET GLOBAL innodb_stats_persistent = ON;,并确保innodb_stats_auto_recalc = ON - 注意:
FORCE INDEX不解决数据倾斜问题 —— 如果某索引值重复率 > 95%,即使强制,也可能被降级为全表扫描
MySQL 8.0 的 use_index_extensions 开关影响什么
这个变量决定优化器是否利用二级索引中隐含的主键字段(即索引扩展),来避免回表。开启后,某些 WHERE + ORDER BY 组合可能直接用二级索引完成排序,不触发 Using filesort。
- 典型场景:查询
WHERE category=1 ORDER BY id,而你建的是(category)索引 —— 开启后,MySQL 会把id当作索引的一部分用 - 默认值:8.0+ 是
ON,5.7 及以前不支持 - 关掉测试对比:
SET SESSION use_index_extensions = OFF;,再看EXPLAIN的Extra字段是否多出Using filesort - 风险点:如果业务依赖旧版行为做性能压测,升级 8.0 后未验证,可能因该开关导致执行计划突变
索引扫描器相关参数真正起作用的前提,是统计信息准、索引设计合理、没有隐式类型转换。调参只是微调,别指望靠改几个变量救活一个没覆盖查询条件的索引。











