MySQL中IN可能走索引,NOT IN通常不走索引;关键取决于字段是否有索引、IN列表是否为确定常量、值数量是否适中(几十个以内)、复合索引是否满足最左前缀,以及优化器代价估算,需用EXPLAIN验证。

MySQL 在使用 IN 或 NOT IN 时,是否能走索引、走哪个索引,主要取决于字段是否有索引、IN 列表的结构(是否为常量、是否含 NULL)、以及优化器对查询代价的估算。不是所有 IN 都能高效利用索引,NOT IN 更容易“放弃”索引。
IN 查询与索引使用的关键条件
IN 本身是可索引的,但前提是:左侧字段有可用索引,且右侧是**确定的常量列表**(如 id IN (1,3,5)),不含子查询或表达式(如 id IN (SELECT x FROM t) 会触发不同执行逻辑)。
- 若列上有单列索引(如
INDEX (status)),status IN ('active','pending')通常能走该索引,执行方式类似多个等值查找合并 - 若
IN值过多(例如上千个),优化器可能判定全表扫描成本更低,主动放弃索引——可通过EXPLAIN中的key和rows字段验证 - 复合索引中,
IN只能用到最左前缀的**连续部分**:例如索引为(a,b,c),WHERE a = 1 AND b IN (2,3) AND c = 4可用全部三列;但WHERE a IN (1,2) AND c = 4只能用到a,c无法跳过b使用
NOT IN 的索引风险更高
NOT IN 很难高效走索引,尤其当右侧列表含 NULL 时,结果语义会变为全空(因为 expr NOT IN (1, NULL) 永远不成立),MySQL 会直接放弃使用索引,转为全表扫描。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 即使
NOT IN列表全是非空常量(如id NOT IN (1,2,3)),优化器也往往不选索引——因需排除多个值,B+树索引不擅长“范围排除”,代价估算倾向于扫描 + 过滤 - 替代方案更可靠:用
LEFT JOIN ... IS NULL或NOT EXISTS,它们更容易触发索引访问,特别是关联字段有索引时 - 例如想查“不在黑名单中的用户”,比起
SELECT * FROM users WHERE id NOT IN (SELECT uid FROM blacklist),改写为SELECT u.* FROM users u LEFT JOIN blacklist b ON u.id = b.uid WHERE b.uid IS NULL通常性能更好且稳定走索引
如何确认实际是否用了索引
别依赖直觉,必须看 EXPLAIN 输出:
- 检查
key列是否显示索引名(NULL表示没走索引) - 观察
type:理想是range(IN常见)或ref;若为ALL就是全表扫描 - 注意
Extra中是否出现Using index condition(ICP 下推)或Using where; Using index(覆盖索引),这些说明索引被有效利用 - 对
NOT IN子查询,还要看select_type是否为DEPENDENT SUBQUERY,这类嵌套易导致重复执行和索引失效
实用建议:让 IN/NOT IN 更“索引友好”
核心思路是减少优化器的“不确定性”,给它明确、低成本的路径:
-
IN列表尽量控制在几十个以内;超百项考虑拆成多次查询或临时表 - 避免
NOT IN+ 子查询;优先改用NOT EXISTS(支持相关子查询索引)或LEFT JOIN - 确保字段类型严格匹配:比如
INT列不要传字符串(id IN ('1','2')可能触发隐式转换,使索引失效) - 对高频
IN查询场景,可考虑将常用值集预存在内存表或 Redis,减少数据库压力

















