一、 索引的"终结者":多个普通范围查询(>, <, BETWEEN)
这是最基础也最致命的坑。在联合索引 (a, b) 中,索引的威力在遇到第一个"范围"时就会发生截断。
为什么会失效?
联合索引的底层是 B+ 树,它是按顺序排列的。
- 等值查询 :如果
a = 1,那么在a=1的范围内,b是绝对有序的。 - 范围查询 :如果
a > 1,那么在命中的所有记录里,b变成了局部有序、全局无序。
后果:
MySQL 只能利用索引定位到 a 的范围,对于 b 字段,它无法在索引树上进行"跳跃查找",只能通过 Index Condition Pushdown (ICP) 逐条扫描过滤,甚至直接导致 Filesort(文件排序)。
二、 索引的"幸存者":为什么多个 IN 条件能生效?
很多同学以为 IN 也是范围查询,其实在 MySQL 优化器眼里,IN 更像是一系列的等值点。
为什么有效?
对于 WHERE a IN (1, 2) AND b IN (3, 4),MySQL 会将其拆解为 4 个精确的坐标点:
(1, 3)(1, 4)(2, 3)(2, 4)
因为每一个组合都是确定的"点",MySQL 可以顺着 B+ 树的逻辑,精确地跳转到这 4 个位置。此时,a 和 b 的索引都是生效的。
三、 深度思考:既然 IN 索引生效,为什么还要避免多个 IN?
既然 IN 不会截断索引,那是不是可以随便写?绝对不是。 多个 IN 会带来另一个恐怖的问题:组合爆炸与 Index Dive 成本。
1. 笛卡尔积的诅咒
每一个 IN 列表的增加,都会让扫描路径呈指数级增长:
a IN (10个值)×\times×b IN (10个值)×\times×c IN (10个值)= 1000 条路径。
2. Index Dive(索引下潜)的沉重代价
在 SQL 真正运行前,优化器需要计算成本。为了预估这 1000 条路径分别有多少行数据,它会执行 1000 次 Index Dive ------ 也就是 1000 次深入 B+ 树叶子节点的探测。
- 编译开销:仅仅是生成执行计划,就可能消耗掉数百毫秒的 CPU。
- 并发瓶颈:在高并发下,这种"探路"动作会瞬间耗尽数据库的线程资源。
3. 优化器的"背叛"
当组合数超过阈值(eq_range_index_dive_limit,通常是 200)时,MySQL 会为了保命而放弃精准探测,转而使用索引统计信息进行模糊预估。
- 风险 :一旦估算错误,优化器可能会"脑抽"放着索引不用,跑去执行全表扫描。