为何MySQL优化器不使用联合索引全部3列?(Percona MySQL 5.7)
让我来拆解你遇到的这个问题——我在日常优化MySQL查询时经常碰到类似的索引选择困惑,下面分两部分给你解释:
一、为什么优化器默认不使用联合索引的全部3列?
MySQL(包括Percona分支)的优化器是基于成本估算来选择执行计划的,它会计算不同执行路径的IO、CPU成本,然后选最低的那个。你遇到的情况,大概率是这几个原因之一:
统计信息不准确
如果表的统计信息过时(比如最近有大量数据插入/更新),优化器会错误估算BASE+QUOTE过滤后的行数。比如它误以为只通过前两列就能过滤出极少的数据,即使不用第三列索引,直接取出这些行再排序取最新值的成本,比走完整联合索引更低。这种情况你可以先执行ANALYZE TABLE your_table_name;更新统计信息,再看执行计划是否变化。第三列的过滤/排序收益被低估
你的查询是取指定时间段前的最新数据,推测第三列是时间类型(比如update_time),索引顺序是(BASE, QUOTE, time_col)。优化器可能觉得:前两列已经把数据范围缩得很小了,哪怕在内存里对这几条数据做排序(ORDER BY time_col DESC LIMIT 1),成本也比走完整索引的IO成本低。但实际上,走完整索引可以直接利用索引的有序性,定位到符合条件的最后一行,完全避免排序操作——这时候优化器的成本模型判断出现了偏差。前两列的选择性过高
如果BASE+QUOTE的组合几乎是唯一的(比如接近唯一键),优化器会认为加上第三列索引的收益微乎其微,没必要再利用第三列来缩小范围或排序,所以只用到前两列。
二、是否应该采用强制指定索引的查询语句?
这个要分场景来看,不能一概而论:
可以用的情况
如果强制索引后,查询性能确实有明显提升(比如从几十毫秒降到几毫秒),而且这个查询是系统的高频核心查询,那么短期内在确认数据分布不会剧烈变化的前提下,可以使用FORCE INDEX (IDX_UK)来锁定执行计划。
更稳妥的替代方案
强制索引是“硬编码”的优化手段,后续如果表的数据分布、结构发生变化(比如BASE+QUOTE的选择性下降,或者新增了更合适的索引),优化器无法自动调整执行计划,反而可能导致性能退化。所以更推荐你先尝试这些方法:
- 用
EXPLAIN对比强制索引和默认执行计划的rows(估算行数)、Extra(是否有Using filesort等),确认优化器的估算偏差; - 尝试将查询改成覆盖索引查询:如果你的
SELECT语句不需要所有列,只取出需要的字段,并且这些字段都在联合索引里(或者把需要的字段加入索引做成覆盖索引),优化器会更倾向于选择完整的联合索引; - 开启
optimizer_trace查看优化器的决策细节:执行SET optimizer_trace="enabled=on";后运行查询,再查information_schema.optimizer_trace,就能看到优化器排除完整索引的具体原因,针对性调整。
内容的提问来源于stack exchange,提问作者mr_blond

