IS NULL 能走索引是因为满足索引物理结构与优化器成本模型;需有单列索引、NULL占比合理、优化器判定更优;联合索引最左列为IS NULL时失效,因NULL不参与B+树排序而挂入独立链表。

IS NULL 能走索引,不是因为“MySQL突然支持了”,而是因为它在特定条件下满足了索引的物理结构和优化器的成本模型——能定位、够便宜、有依据。
IS NULL 什么时候真能走索引?
关键看三件事:该列有没有单列索引、NULL值占比是否足够低(或足够高)、优化器是否认为走索引比全表扫描更省。
-
col上建了单列索引(如INDEX idx_col (col)),且col允许为NULL(即Null列在SHOW INDEX中显示为YES) - 当
col IS NULL匹配的行数远少于总行数(比如 95%),优化器大概率选索引——前者靠“ref”定位,后者靠“range”扫NULL密集区 - MySQL 5.7+ 尤其是 8.0.13+ 对
IS NULL的索引下推(ICP)支持更好,Extra: Using index condition出现即说明索引层做了过滤
为什么联合索引里最左列为 IS NULL 就失效?
InnoDB 的联合索引键值是按字段顺序拼接排序的,一旦最左列值为 NULL,整个键就无法参与B+树比较——它不被写入有序键序列,而是被单独挂到叶子节点末尾的 NULL slot list 链表里。
-
INDEX (a, b),查询WHERE a IS NULL AND b = 1:优化器无法从B+树根节点开始定位子树,因为a IS NULL不提供任何范围边界,key字段在EXPLAIN中会是NULL - 但
WHERE a = 1 AND b IS NULL可以:先用a = 1定位子树,再在该子树内遍历并过滤b IS NULL,type通常是ref或range - 如果非要查
a IS NULL AND b = 1,得把a拆出来建单列索引,或改用生成列:ALTER TABLE t ADD COLUMN a_is_null TINYINT GENERATED ALWAYS AS (a IS NULL) STORED,再给它建索引
EXPLAIN 看不出问题?那就 FORCE 一把
别只信直觉或文档,EXPLAIN 是唯一真实依据。但有时优化器“保守”,你得逼它亮底牌:
- 先跑
EXPLAIN SELECT * FROM t WHERE col IS NULL,看key是否非空、rows是否显著小于SELECT COUNT(*) FROM t - 再跑
EXPLAIN SELECT * FROM t FORCE INDEX (idx_col) WHERE col IS NULL,对比rows是否大幅下降——如果下降明显,说明优化器误判了成本 - 注意:强制索引可能掩盖统计信息陈旧问题,记得
ANALYZE TABLE t更新基数(Cardinality)
真正容易被忽略的点
很多人盯着“能不能走索引”,却漏掉了两个硬伤:
-
IS NULL查询即使走了索引,也无法利用索引排序:因为NULL slot list是插入序链表,不是有序结构,ORDER BY col依然要 filesort -
覆盖索引对IS NULL无效:叶子节点里NULL记录不存完整字段值,必须回表,Extra里不会出现Using index(只有Using index condition)


















