explain显示type: all和key: null是优化器主动放弃索引的明确信号,因or条件导致合并多索引开销大,成本估算后判定全表扫描更优;即使a、b各有单列索引,mysql默认不拼用,需满足高选择度、无函数、类型匹配等严苛条件才可能启用index_merge。

EXPLAIN 显示 type: ALL 和 key: NULL 是什么信号
这不是 bug,是优化器主动放弃索引的明确表态。它看到 OR 条件后做了成本估算:合并多个索引片段要回表、去重、排序,随机 IO 开销可能远超全表顺序扫描。尤其当预估结果集占表比例较大(比如 >15%)、字段选择度低(如 gender 只有 'M'/'F'),或存在隐式转换(varchar 字段查数字)时,type: ALL 就是它的最终决定。
WHERE a = 1 OR b = 2 即使 a 和 b 都有单列索引,为什么仍不走索引
MySQL 不会自动把两个单列索引“拼起来”用——它默认只选一个索引,除非启用并命中 index_merge。而这个机制在 8.0+ 虽默认开启,但仅对 AND 场景更友好;对 OR,它仍要求两侧条件都高选择度、无函数包裹、类型严格匹配,且成本模型必须判定合并比全扫便宜。现实中,哪怕你建了 idx_a 和 idx_b,只要其中一边是 LIKE '%abc' 或 IS NULL,整条 OR 就直接被跳过。
用 UNION ALL 重写时最容易踩的三个坑
- 字段不一致:每个子查询的列数、顺序、类型必须完全相同,否则报错
ERROR 1222;别用SELECT *,显式写出列名 - 漏掉补充谓词:比如原查
WHERE status = 'paid' OR user_id IN (1001,1002),拆成两个子查询后,第二条得补上AND status != 'paid'(除非业务确认互斥) -
LIMIT和ORDER BY放错位置:不能只在外层加,否则分页会漏数据;需按实际语义重算偏移,或在子查询里分别加(但要注意去重逻辑是否受影响)
什么时候该建复合索引而不是硬拆 OR
如果 OR 实质是同一维度的离散值匹配(比如 status = 'draft' OR status = 'pending'),或者字段间有强业务约束(如 category = 'A' OR category = 'B' 且 category 总是单值),建复合索引反而更稳。例如 INDEX idx_status (status) 就能覆盖全部等值判断,不需要 index_merge 或 UNION ALL。真正麻烦的是跨字段、弱相关、或带范围/模糊条件的 OR —— 那才是拆和建都得仔细权衡的地方。











