news 2026/8/30 7:15:55

两个字段都建了单列索引,为什么加了 OR,执行计划还是全表扫描?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
两个字段都建了单列索引,为什么加了 OR,执行计划还是全表扫描?

写 SQL 使用OR条件是非常常见的场景,为了优化这类查询,会特意为phoneemail两个字段分别创建单列索引。

SELECT * FROM users WHERE phone = '13800000000' OR email = 'xiaofu@qq.com';

但我们使用EXPLAIN查看该语句的执行计划,往往会发现type字段显示为ALL,说明 MySQL 最终还是走了全表扫描,没有使用我们创建的索引。

奇怪吧,明明两个字段都有索引,为什么用OR之后索引却失效了?

MySQL 优化器的成本计算

理解为什么索引失效前,先理解 MySQL 查询优化器的工作原理。MySQL 是基于成本的优化器,它在生成执行计划时,会估算各种执行路径的成本值,最终选择成本最低的路径。

传统的关系型数据库,MySQL 默认的读取路径一次查询只能使用一个索引。假设我们将查询限制在phone索引上,那么优化器会通过phone二级索引树快速定位到主键值,再通过主键值回表获取完整的行记录。

但在OR条件下,查询的语义是并集,也就是满足phone = '13800000000'或者email = 'xiaofu@qq.com'的数据都需要被找出来。

如果优化器只选择走phone索引,确实能快速找到phone匹配的行;但由于满足email条件的行可能散落在表的其他位置,为了不漏掉数据,MySQL 在通过phone索引查出部分数据后,仍然不得不对整张表进行一次全表扫描,找出满足email = 'xiaofu@qq.com'且不与phone重合的数据。

这时候优化器会对比两种执行路径的成本:

  • 路径 A(单索引 + 回表 + 全表扫描):扫描phone索引 + 回表获取记录 + 全表扫描查找email的记录。

  • 路径 B(直接全表扫描):从头到尾扫描一次全表,边扫描边过滤满足phoneemail条件的记录。

回表属于随机 I/O,而全表扫描属于顺序 I/O。在 MySQL 的成本计算中,一次随机 I/O 的权重默认是顺序 I/O 的几倍。如果回表的数据行数稍微多一点,路径 A 的估算成本就会远超路径 B。所以优化器会果断放弃索引,直接走全表扫描。

为什么索引合并没生效?

有同学会问:MySQL 不是有索引合并(Index Merge)机制,它能同时用两个索引,最后在内存里把结果合并吗?

OR条件下,MySQL 确实有个index_merge_union索引并集算法,它的处理流程:

  1. 二级索引扫描:通过 phone 索引树扫描出满足条件的主键 ID 集合 S1。由于二级索引的叶子节点本身是按照二级索引键值排序的,相同的二级索引键值,其叶子节点存储的主键 ID 默认是递增有序的。

  2. 二级索引扫描:通过 email 索引树扫描出满足条件的主键 ID 集合 S2,同样的其内部主键 ID 也是有序的。

  3. 并集去重:内存中将 S1 和 S2 进行去重合并,得到最终的主键集合。因为 S1 和 S2 天生有序,MySQL 可以使用高效的双指针归并算法以 O(N) 的时间复杂度快速完成合并。

  4. 有序回表:利用合并后的主键集合进行回表查询。这时主键 ID 已经是去重且有序的,回表操作可以从随机 I/O优化为顺序 I/O,极大地提高了读取效率。

都有这个机制,为什么实际开发很难看到它生效?这主要受限于几个硬性约束和成本考量:

算法对主键有序性的要求

index_merge_union算法之所以高效,核心在于并集去重操作能在 O(N) 时间复杂度内完成。这要求S1 和 S2 两个集合在扫描出来时必须是天生有序的

只有在等值查询,如phone = '138...'取出的主键 ID 才是按照主键大小递增排序的。

一旦出现范围查询,如phone LIKE '138%'phone > '138',由于二级索引键值不同,即使索引字段有序,但对应的主键 ID 在索引页中也是无序交错的。

这样 MySQL 无法直接利用双指针进行并集,必须引入Sort-Union算法,即先在内存中对主键 ID 进行排序,然后再做并集。但在内存排序对 CPU 和内存开销很大,优化器在估算成本后,通常会放弃索引合并,直接选择全表扫描。

优化器成本估算的临界值

即便OR两边都是等值查询,字段都有索引,优化器依然会很细致。

回表比例达到一定阈值(一般取决于表的数据量、页大小及系统负载),回表的随机 I/O 成本会呈指数级上升。优化器计算出索引合并的成本比一次性的全表扫描还要高,必然会选择全表扫描。

硬性失效

OR的底层逻辑是必须获取满足任意一方的所有数据:

隐式类型转换:如果 phone 在表中是VARCHAR类型,但在 SQL 中写成了数字WHERE phone = 13800000000,MySQL 会隐式地将字段值转换为浮点数再做比较,导致 phone 索引失效。

既然 phone 无法走索引,MySQL 就必须通过全表扫描来找出满足 phone 条件的行,整个查询因此退化为全表扫描。

包含未建索引的列:如果 SQL 包含没有索引的字段WHERE phone = '138...' OR age = 18,因为age没有索引,数据库无论如何都要进行全表扫描以过滤age = 18的数据,所以phone索引同样会被放弃。

大厂的 SQL 优化方案

为了规避OR的索引失效和优化器成本估算不准,实际开发可以用以下两种更稳妥的优化方案。

UNIONUNION ALL代替OR(首选)

这是我最推荐、执行计划最稳定的改写方式,我们可以将OR查询拆分为两个独立的子查询,然后使用UNION进行连接:

SELECT * FROM users WHERE phone = '13800000000' UNION ALL SELECT * FROM users WHERE email = 'xiaofu@qq.com';

为什么这种方案更优?

  1. 没有单索引限制:拆分后两个子查询是完全独立的,第一个子查询可以稳定、高效地使用 phone 索引,第二个子查询可以稳定使用 email 索引。

  2. 避免优化器估算失准:单索引查询的成本估算非常精准,MySQL 不用去纠结复杂的Index Merge成本。

  3. 优先使用UNION ALL提升性能:UNION会在内存中创建一个临时表,对结果集进行去重排序,带来额外的 CPU 和内存开销。如果业务上可以容忍重复数据,或者逻辑上两个条件的结果集本身就是互斥的,例如 phone 和 email 匹配到的不可能是同一行,强烈建议使用 UNION ALLUNION ALL只做结果集拼接,不进行任何去重和排序性能高。

覆盖索引

如果你的业务场景不需要SELECT *,只需要获取索引列本身

SELECT id, phone, email FROM users WHERE phone = '13800000000' OR email = 'xiaofu@qq.com';

由于查询的字段(idphoneemail)已经全部包含在二级索引中,MySQL扫描索引后不需要进行回表。 没有了随机 I/O 成本,索引扫描和内存合并的开销就变得极廉价,MySQL 优化器 100% 会触发index_merge_union避免全表扫描。

说在最后

SQL 优化的核心,就是让 SQL 的执行路径更简单,增加 SQL 查询的确定性。包含OR条件的组合查询,经常因为回表成本的权衡,导致优化器选择保守的全表扫描。

多索引 OR 查询建议使用UNION ALL进行改写,将复杂的、充满不确定性的多条件 OR 查询,拆解为确定性更高、执行路径更清晰的单索引查询,可以保证 SQL 查询的稳定性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 4:33:46

Agent Skills 入门到实战:从 Prompt 到可复用技能封装

Agent Skills 这个概念,最近讨论热度很高,但很多人还是把它当成普通 Prompt 的升级版,或者跟 AI Agent 混在一起谈。我先把结论放在前面:Agent Skills 本质上是给 AI Agent 准备的一套“标准作业流程 工具脚本”,让 A…

作者头像 李华
网站建设 2026/8/30 6:59:48

AI时代开发者进阶指南:从Prompt到大模型工程实践

John Henry 这个名字,在欧美民间传说里代表一位与蒸汽锤比赛凿石头的铁路工人。他赢了比赛,却因为过度透支倒在了终点线上。这个一百多年前的寓言,放在今天几乎成了“程序员 vs AI 编程工具”的原始模板。只是这一次,角色变了&…

作者头像 李华
网站建设 2026/8/29 4:30:43

GraphRAG实战:基于代码知识图谱的代码库问答实现

做代码库问答和文档问答有一个很明显的差别:一份技术文档可以按段落切块后直接放进向量库,效果往往已经够用;但一套源码里,一个函数只有几十行,却可能被几十个地方调用,它的“含义”是由调用方、被调用方、…

作者头像 李华
网站建设 2026/8/29 4:30:40

硬盘健康监控与故障预警:用Hard Disk Sentinel看懂SMART数据

很多人遇到电脑突然变慢、蓝屏、文件打不开时,第一个反应是重装系统,第二个反应是清灰换硅脂。真正的问题往往被忽略了:那块硬盘可能已经在 SMART 日志里连续报警了好几周,只是没人去看。硬盘是最会“隐瞒病情”的硬件&#xff1a…

作者头像 李华
网站建设 2026/8/29 4:30:34

AI应用盈利难?从算力成本到工程优化的实战指南

这两年 AI 成了整个技术圈乃至投资圈最热的关键词。从大模型刷榜到各类 Agent 应用落地,几乎每周都有新模型、新工具发布。但与此同时,一个声音也越来越清晰:AI 行业看起来很热闹,真正靠客户付费赚到钱的公司却不多。有观点甚至直…

作者头像 李华