IS NULL 可能走索引,取决于字段是否允许NULL、索引类型及版本;5.7+二级索引中NULL被视为最小值,WHERE col IS NULL可能命中索引,但需EXPLAIN验证。

IS NULL 走不走索引?先看执行计划
MySQL 对 IS NULL 的索引支持取决于字段是否允许为 NULL、索引类型和版本。5.7+ 的二级索引(B+Tree)默认会把 NULL 当作最小值存进去,所以 WHERE col IS NULL 是可能走索引的——但前提是这个索引是“有效覆盖”的,不是被优化器判定为“全表扫描更便宜”。
实操建议:
- 用
EXPLAIN SELECT * FROM t WHERE col IS NULL看key和rows字段,别猜 - 如果
col是主键或唯一索引的一部分,IS NULL一定不走索引(因为主键/唯一索引不允许 NULL) - 复合索引里,
IS NULL只能用于最左前缀中“连续非 NULL 条件之后的第一列”,比如索引(a, b, c),WHERE a = 1 AND b IS NULL可能走,但WHERE a IS NULL AND b = 1基本不走
IS NOT NULL 为什么经常不走索引?
IS NOT NULL 在绝大多数情况下等价于“排除极少数 NULL 值”,如果该列 NULL 比例很低,优化器会直接放弃索引、走全表扫描——因为随机 IO 成本比顺序扫描高得多。
常见错误现象:
- 明明建了
INDEX(col),WHERE col IS NOT NULL却显示key=NULL - 加了
FORCE INDEX强制走索引,查询反而变慢
原因很简单:索引只加速“定位”,不加速“排除”。当你要扫出 99% 的行时,B+Tree 跳来跳去读页,不如直接顺序读数据页快。
替代思路:
- 如果业务上 NULL 是异常值(比如只有几条),改用
WHERE col > 0或具体范围条件,更容易命中索引 - 对高频
IS NOT NULL查询,考虑在写入时补默认值(如0、''),并加NOT NULL约束,让优化器更敢用索引
NULL 值导致索引失效的隐藏场景
不是所有“看起来用了索引”的查询都真受益。NULL 会在几个关键位置悄悄破坏索引效率:
- 在
ORDER BY col中,如果col允许 NULL,MySQL 默认把 NULL 排在最前面;若你加了ORDER BY col DESC,NULL 又跑到最后——这会导致无法复用索引排序,触发Using filesort -
GROUP BY col时,所有 NULL 被视为同一组,但索引本身不保证 NULL 的物理连续性,分组过程仍可能回表或临时表 - 联合索引中某列为 NULL,可能导致后续列的索引值无法被下推,比如
INDEX(a, b),WHERE a IS NULL AND b = 5通常不会用到b的索引部分
检查方法:在 EXPLAIN 输出里盯住 Extra 列,出现 Using filesort 或 Using temporary 就要警惕 NULL 是否搅局。
建表阶段怎么防 NULL 带来的索引麻烦?
与其后期调优,不如从源头控制 NULL 的出现。这不是教条主义,而是 MySQL 索引机制决定的现实约束。
实操建议:
- 除明确需要表示“未知/缺失”的字段(如
end_time),其他一律加NOT NULL+ 合理默认值(如created_at DEFAULT CURRENT_TIMESTAMP) - 字符串字段慎用
DEFAULT NULL,宁可用DEFAULT '';数值字段宁可用DEFAULT 0,除非 0 本身有业务含义 - 已有表想改,别直接
ALTER TABLE ... MODIFY col INT NOT NULL——会锁表。先UPDATE补 NULL 值,再ALTER加约束,分步做
真正难处理的,永远是那些“历史遗留的、NULL 含义模糊、又不敢随便改默认值”的字段。它们不会报错,但会让每个 IS NULL 都像在走钢丝。










