MySQL未走索引主因是单列索引不支持多列独立条件的高效匹配,需建有序联合索引;index_merge是兜底策略,性能差且不稳定,应优先优化为覆盖式联合索引。

MySQL 为什么没走你建的索引?
不是所有 WHERE 条件组合都能触发单个索引——当查询含多个独立列条件(比如 status = 1 AND type = 'user' AND created_at > '2024-01-01'),而你只在 status 上建了索引,MySQL 很可能直接全表扫描。
根本原因是:B+ 树索引本质是有序结构,单索引只能高效支持「最左前缀」匹配。三个等值条件分散在不同列,又没建联合索引,优化器发现用不上,就放弃。
- 常见错误现象:
EXPLAIN显示type=ALL或key=NULL,哪怕每列都有单列索引 - 别指望 MySQL 自动“拼”几个单列索引一起用——它确实能,但叫
index_merge,代价高、限制多、默认常被禁用 - 联合索引更可靠:
CREATE INDEX idx_status_type_created ON table_name (status, type, created_at),顺序按查询频率和区分度调整 - 注意:如果其中一列是范围查询(如
created_at > ...),它右边的列在索引中就失效了(无法用于 WHERE,但可用于 ORDER BY / GROUP BY)
index_merge 是什么,什么时候真会用上?
index_merge 是 MySQL 在实在没更好选择时的“兜底策略”,把多个单列索引的结果集做交集/并集/并集去重(intersection/union/sort_union)。它不快,也不稳定。
- 典型触发场景:
WHERE a = 1 OR b = 2(OR 条件 + 各自有单列索引),或WHERE a = 1 AND b = 2(AND,但没联合索引且优化器评估index_merge比全表扫便宜) -
EXPLAIN中看到type=index_merge和key=xxx,yyy就是它在干活 - 性能影响明显:要读多次索引、做内存合并、还常触发临时表;5.7+ 默认开启,但 8.0 对
index_merge_intersection更激进,有时反而选错 - 兼容性坑:某些 ORM 自动生成的
IN查询(如id IN (1,2,3))可能意外触发index_merge,不如显式建好覆盖索引
怎么判断当前查询是否该用 index_merge?
别猜。用 EXPLAIN FORMAT=JSON 看优化器真实决策依据,比看 EXPLAIN 表格更准。
- 执行
EXPLAIN FORMAT=JSON SELECT ...,重点找"using_index_merge": true和"chosen_range_access_summary"里的成本估算 - 对比建联合索引后的
EXPLAIN:如果type=range+key=your_new_composite_idx,且rows显著下降,说明index_merge是次优解 - 注意
index_merge的隐式开关:optimizer_switch='index_merge=on',线上环境别盲目关,先确认是否真由它导致慢 - 一个硬指标:如果
index_merge的rows总和远大于表总行数(比如两个索引各返回 10w 行,交集后只剩 100 行),那合并开销大概率不划算
联合索引设计时最容易忽略的三件事
建了联合索引不等于万事大吉。顺序、类型、覆盖性,漏一个都可能让优化器绕过它。
- 等值条件列放前面,范围条件列放后面:比如
WHERE tenant_id = ? AND status IN (?,?) AND created_at > ?,索引应为(tenant_id, status, created_at),而不是反过来 - 避免在索引列上用函数或表达式:
WHERE DATE(created_at) = '2024-01-01'会让整个索引失效;改用created_at >= '2024-01-01' AND created_at - 尽量覆盖查询字段:如果
SELECT id, name, status,而索引是(status, id),那name还得回表;加到索引里变成(status, id, name),就能走Using index
复杂点在于:业务查询条件永远在变,今天最优的联合索引,明天加个新筛选字段就可能失效。所以得定期用 slow_query_log + pt-query-digest 抓真实高频查询,而不是只盯着开发写的 SQL 想当然建索引。










