is null 查询能否走索引取决于字段是否允许null、索引结构及数据分布;允许null且索引合理时可走索引,但null占比过高或统计信息过期会导致优化器放弃索引。

IS NULL 查询走不了索引?先看字段有没有允许 NULL
MySQL 对 IS NULL 的索引支持取决于字段定义本身。如果字段是 NOT NULL,那 WHERE col IS NULL 永远返回空结果,优化器可能直接跳过索引;但如果字段允许 NULL,且有索引,多数情况下能用上——前提是索引类型和查询写法没踩坑。
- MyISAM 和 InnoDB 都支持对
NULL值建立 B+ 树索引,但索引项里会把NULL当作一个特殊值存储(不是跳过) - 唯一索引(
UNIQUE)中,多个NULL是允许的,它们不参与“唯一性校验”,这点常被误认为索引失效 - 如果字段是
TINYINT NOT NULL DEFAULT 0,却用IS NULL查,实际查不到数据,执行计划里显示type: const或直接Impossible WHERE,这不是索引问题,是逻辑矛盾
联合索引里 IS NULL 能不能命中最左前缀?
能,但只在特定顺序下生效。联合索引 (a, b, c) 中,WHERE a = 1 AND b IS NULL 可以走索引;但 WHERE a = 1 AND c IS NULL 就不行——因为 c 不在最左连续段里。
-
IS NULL在联合索引中等价于一个确定的“值比较”,不是范围扫描,所以只要左侧列都满足等值条件,当前列为IS NULL仍可继续使用索引 - 但
WHERE b IS NULL单独出现,或WHERE a > 1 AND b IS NULL,就会中断最左前缀,索引只能用到a(如果是范围),b后面全丢弃 - 实测:建索引
INDEX idx_ab (a,b),执行EXPLAIN SELECT * FROM t WHERE a = 5 AND b IS NULL,key_len显示用了全部两个字段长度,说明命中了
为什么有时候 EXPLAIN 显示 key 有值,但 rows 却扫全表?
这是最常被忽略的性能陷阱:索引确实被选中了,但 MySQL 评估后认为回表成本太高,或者 NULL 值占比过大,干脆走全表扫描更划算。
- 如果某列 NULL 率超过 ~20%,优化器大概率放弃该索引,哪怕语句写得再“合规”
-
ANALYZE TABLE没及时更新统计信息时,优化器可能误判 NULL 分布,导致错误选择执行计划 - 显式强制索引(
USE INDEX (idx_name))不一定能解决问题,反而可能让查询变慢,因为绕过了优化器的代价估算 - 验证方式:对比
EXPLAIN FORMAT=JSON中的rows和实际SELECT COUNT(*),如果前者接近后者,基本就是“索引形同虚设”
替代方案:用默认值 + 普通等值查询更稳
比起依赖 IS NULL 走索引,把业务上“无意义”的空值统一映射为一个明确的哨兵值(比如 -1、'N/A'、0000-00-00),再配合 NOT NULL 约束,往往更可控、更易优化。
- 避免 NULL 引发的三值逻辑(
TRUE/FALSE/UNKNOWN),减少WHERE条件漏数据风险 - 索引密度更高,
COUNT(col)和COUNT(*)行为一致,统计更直观 - 迁移成本低:加新列、UPDATE 填充、改应用层写入逻辑,比反复调优
IS NULL查询更省心 - 注意:时间类型字段慎用
0000-00-00,MySQL 5.7+ 默认 SQL mode 下会报错,建议用'1970-01-01'或业务可识别的极小日期
NULL 值索引不是不能用,而是它的行为高度依赖数据分布、统计信息、索引结构和优化器版本。一旦发现 IS NULL 查询变慢,优先检查 NULL 比例和 ANALYZE TABLE 是否最新,而不是立刻加索引或改 SQL。










