mysql中is null查询能否走索引取决于索引类型、字段是否允许null、版本及存储引擎;innodb中null不参与b+树排序,高null比例时优化器可能放弃索引,5.7+后支持增强,但联合索引最左前缀为null时匹配中断,unique索引因允许多个null而难以优化is null查询。

MySQL 中 IS NULL 查询能否走索引,取决于索引类型、字段是否允许为 NULL、以及 MySQL 版本和存储引擎(主要是 InnoDB),不是一概“失效”——但确实容易踩坑。
为什么 IS NULL 有时不走索引
InnoDB 的 B+ 树索引中,NULL 值被特殊处理:它不参与排序比较,也不像普通值那样被写入索引页的有序键值序列。对单列索引而言,如果该列定义为 ALLOW NULL,MySQL 可能选择跳过索引、直接全表扫描——尤其当 NULL 比例高、优化器估算走索引成本更高时。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,即使该列明明建了索引。
- 不是所有
IS NULL都一定不走索引;5.7+ 和 8.0 中,InnoDB 对IS NULL的支持已明显增强,尤其是单列二级索引 - 联合索引中,若最左前缀列是
NULL,整个匹配就中断(例如INDEX(a,b),WHERE a IS NULL AND b = 1无法利用该索引) -
UNIQUE索引对NULL的处理更特殊:多个NULL值不违反唯一性约束,但这也导致优化器难以用它加速IS NULL查找
IS NULL 能否走索引的实操判断方法
别猜,直接用 EXPLAIN + SHOW INDEX 验证,重点看三处:
- 执行
SHOW INDEX FROM table_name WHERE Key_name = 'your_index';,确认Null列值为YES(说明该列允许NULL) - 运行
EXPLAIN SELECT * FROM table_name WHERE column_name IS NULL;,观察key是否非空、rows是否显著小于全表行数 - 对比加
FORCE INDEX后的执行计划:EXPLAIN SELECT * FROM table_name FORCE INDEX (idx_col) WHERE column_name IS NULL;,看是否强制走索引后rows下降
注意:MySQL 8.0.13+ 对函数索引支持更好,COALESCE(column_name, 'NULL_MARKER') 可绕过原生 NULL 问题,但需业务层配合统一标记逻辑。
真正有效的解决路径(按优先级)
不要一上来就加组合索引或改表结构,先看是否可收敛问题源头:
-
把字段设为
NOT NULL并配默认值:如业务允许,ALTER TABLE t MODIFY col INT NOT NULL DEFAULT 0;——这是最彻底的解法,IS NULL查询自然消失,索引完全可用 -
用覆盖索引减少回表开销:即使
IS NULL走了索引,若还需回主键查其他字段,性能仍差;建INDEX(col, other_col)让查询能直接从索引取到全部需要的字段 -
避免在高频查询字段上存大量
NULL:比如状态字段用0/-1代替NULL,既提升索引效率,也减少应用层判空逻辑 - 不到万不得已,**别依赖
COALESCE函数索引**:它会让写入变慢(函数计算开销)、且应用层必须严格保持一致的填充逻辑,容易漏掉边缘 case
联合唯一索引里含 NULL 的特殊陷阱
这是最容易被忽略的复杂点:MySQL 规定,UNIQUE 约束下,多列组合中只要任意一列是 NULL,整行就不参与唯一性校验。也就是说,(1, NULL) 和 (1, NULL) 是允许重复插入的。
后果很直接:
-
SELECT ... WHERE a = 1 AND b IS NULL几乎必然全表扫描,因为优化器知道b IS NULL匹配的行在索引中无序、不可定位 - 即便你建了
UNIQUE(a,b),也无法靠它加速IS NULL查询,反而可能误导你认为“有索引就安全” - 修复方式只有两个:要么
NOT NULL+ 默认值,要么拆成单独的状态表(如nullable_relations)隔离处理
真正要命的不是“索引失效”,而是失效发生得悄无声息——执行计划看起来用了索引,实际 rows 接近全表,而你直到慢查询报警才意识到。











