问题定义

SQL 语句

SELECT * FROM TBL_XXX WHERE A = "abc" AND B IN (1, 2, 3);

索引
TBL_XXX 表上有 idx_aidx_b 两个非唯一索引。

问题
由于 A、B 非联合索引,因此只会用到 idx_a;当 A 列筛选出的行数很大时,结果集的每一行都要做回表,去判断是否满足 B 列条件,从而造成查询超时。


解决方案

改进后 SQL 语句

SELECT * FROM TBL_XXX WHERE A = "abc" AND B = 1
UNION ALL
SELECT * FROM TBL_XXX WHERE A = "abc" AND B = 2
UNION ALL
SELECT * FROM TBL_XXX WHERE A = "abc" AND B = 3;

原理剖析
通过 EXPLAIN 指令可以发现,三条 SELECT 语句的 Extra 列有的是 Using where、有的是 Using intersect,这是因为 MySQL 根据 B 条件不同的取值计算出了不同的扫描行数,选择了该条件下它所认为的最优方式。

Using intersect 能够对多个扫描结果取交集,这里相当于是对 B 列每个条件得到的结果集 与 A 列分别取了交集做了多次查询,再将最后得到的结果集取并集,从而无需再回表;即使 SQL 语句后面还有 C = "xyz" 的非索引列条件,也仅需在交集上做回表,大幅降低了回表次数。


Using intersect 条件
SQL 语句为多个索引的 AND 条件时,满足:

  • 如果是二级索引,必须是等值查询,且覆盖其中所有索引列
  • 如果是主键索引,可以是范围查询

    SELECT * FROM TBL_XXX WHERE primary_key < 10 AND key_col1 = 20;

参考

  1. 索引合并,能不用就不要用吧!
2025-08-09 技术学习·none