in仅在单字段等值判断且该字段有高选择性索引时才真正比or快;否则可能更慢或失效。

能用 IN 替代的 OR,只在单字段等值判断且该字段有索引时才真正有效;否则强行替换反而掩盖问题,甚至更慢。
什么时候 IN 真的比 OR 快?
MySQL 对 WHERE a = 1 OR a = 2 OR a = 3 这类同字段多等值查询,内部通常会自动重写为 a IN (1, 2, 3) 并走 range 类型索引。但这个“自动优化”只在干净条件下成立:
- 所有条件必须作用于**同一列**,不能混用
status = 'A' OR user_id = 123 - 该列**必须有可用索引**(B+ 树普通索引或主键),且不是低选择性字段(如只有两个值的
is_deleted) - 值列表不宜过大:MySQL 5.7 建议 ≤ 500 项,8.0 可放宽到 1000–2000,再往上容易触发临时表或放弃索引
- 不能含
NULL:col IN (1, 2, NULL)不会匹配col IS NULL行,而col = 1 OR col IS NULL可以——行为不一致 - 字符串值务必加引号,避免隐式类型转换导致索引失效,例如
name IN ('a', 'b')而非name IN (a, b)
为什么有时候 IN 没变快,甚至更慢?
常见误用场景直接破坏索引有效性:
-
IN值来自子查询且结果集过大,如user_id IN (SELECT id FROM log WHERE time > '2025-01-01')—— 子查询返回数万行时,IN退化为全表扫描 - 用
IN替代连续范围,如id IN (1001, 1002, ..., 2000)—— 应改用BETWEEN 1001 AND 2000,语义准、执行计划稳 - 字段无索引,或索引被干扰:比如
UPPER(name) IN ('A', 'B')或name + '' IN ('a', 'b'),函数包裹直接让索引失效 - 混合了可索引和不可索引条件:
status IN ('A','B') OR remark LIKE '%x%'—— 后者拖垮整体,优化器直接放弃索引
怎么验证 IN 是否真起作用?
别信语法简洁,只看执行计划和实际耗时:
- 运行
EXPLAIN SELECT ... WHERE col IN (...),确认type是range或ref,且key显示用了预期索引 - 对比
rows预估值:替换前后应明显下降,若仍接近表总行数,说明没走索引 - 在真实数据量下测响应时间,尤其关注高并发时 CPU 和 I/O 是否突增(
IN值过多可能引发大量随机 IO) - 注意
NOT IN是性能黑洞:它几乎无法走索引,且遇到子查询返回NULL时整个结果为空,应优先用NOT EXISTS或反向LEFT JOIN
最常被忽略的一点:当 OR 涉及不同字段(如 status = 'paid' OR level > 10),IN 完全无能为力,此时唯一可控路径是 UNION ALL 拆分并确保每个分支独立走索引——而不是硬套 IN 掩盖根本问题。











