1. 核心区别:等值遍历 vs. 连续扫描
IN:视为多个等值点(Point Search)
当你使用 IN (1, 2, 3) 时,MySQL 优化器通常会将其处理为:
- 先去找 1 的位置,再去找 2 的位置,最后去找 3 的位置。
- 关键点 :在联合索引
(a, b)中,如果a使用了IN,因为a的每一个值都是确定的,MySQL 可以继续利用索引的有序性去过滤b。
>, <, BETWEEN:视为线段扫描(Range Scan)
当你使用 a > 10 时,MySQL 认为 a 是一个连续的区间。
- 关键点 :虽然
a是有序的,但在a > 10这个大区间内,b是无序 的(只有在a相等的情况下b才有序)。 - 结果 :索引的效能在
a字段就停止了,后续的b字段无法利用索引进行快速定位。
2. 实验对比
假设索引为 (age, score):
场景 A:使用 IN
sql
SELECT * FROM users WHERE age IN (20, 30) AND score > 80;
- MySQL 的动作 :
- 去索引树找
age=20的位置,并在该范围内利用索引寻找score > 80。 - 再去找
age=30的位置,并在该范围内利用索引寻找score > 80。
- 去索引树找
- 结论 :
age和score两个字段都用到了索引。
场景 B:使用 BETWEEN 或 >
sql
SELECT * FROM users WHERE age BETWEEN 20 AND 30 AND score > 80;
- MySQL 的动作 :
- 找到
age=20的起始位置,开始往后扫描,直到age=30。 - 在扫描过程中,它无法通过索引"跳跃"找到
score > 80的记录,只能一条条检查score是否符合要求。
- 找到
- 结论 :只有
age用到了索引,score索引失效。
3. 性能层面的区别
| 特性 | IN (val1, val2) |
>, <, BETWEEN |
|---|---|---|
| 底层分类 | 多个等值查询 (Multiple Equalities) | 范围查询 (Range) |
| 索引连续性 | 不截断后续索引字段 | 截断后续索引字段 |
| 排序 (Order By) | 可能导致索引排序失效(需看具体执行计划) | 必然导致后续字段排序失效 |
| 优化器开销 | 列表值越多,计算索引成本的开销越大 | 成本计算简单 |
4. 特殊情况:MySQL 8.0 的"跳跃扫描" (Skip Scan)
在 MySQL 8.0+ 中,如果联合索引的前缀列区分度很低(比如只有几个枚举值),即使你用了范围查询,优化器有时也会尝试通过"跳跃扫描"来强行利用后面的索引字段。但这种优化是不稳定的,最稳健的方法依然是把范围查询放在索引的最后。
5. 什么时候 IN 也会变成"范围条件"?
虽然 IN 表现得像等值,但如果 IN 列表中的值过多 (超过了 eq_range_index_dive_limit 配置的值),或者它是通过一个复杂的子查询产生的,MySQL 可能会放弃点对点查找,将其降级为范围扫描。此时,它对后续索引列的影响就和 BETWEEN 一样了。