IS NULL是否走索引由优化器成本估算决定:低NULL占比时通常走索引(type为ref/range),高占比时倾向全表扫描;联合索引最左列为IS NULL会中断匹配,UNIQUE索引因允许多个NULL导致统计失真和优化困难。

IS NULL 走不走索引,由优化器成本估算决定
MySQL 不会因为写了 IS NULL 就“禁止”用索引。它真正看的是:走索引查这批 NULL 值,比全表扫快吗?这个判断基于统计信息(比如该列 NULL 占比)、索引类型、是否回表、MySQL 版本等综合成本。所以同一张表、同一个查询,在不同数据分布下,EXPLAIN 结果可能完全不同。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,但字段明明建了单列索引;或者 key 有值,rows 却接近全表行数——说明索引被选中了,但优化器评估后认为回表代价太高,干脆退化成“假走索引真全扫”。
联合索引最左列为 IS NULL,索引直接中断
这是最容易被忽略的硬限制。InnoDB 的 B+ 树排序依赖最左列的确定值,而 NULL 不参与比较,树无法收敛分支。哪怕你写的是 WHERE a IS NULL AND b = 1,且索引是 INDEX(a, b),优化器也会跳过整个索引。
-
INDEX(a, b)下,WHERE a = 5 AND b IS NULL可以走索引(a是确定值,b IS NULL作为等值条件继续匹配) -
WHERE a IS NULL AND b = 1不行 —— 最左列断了,后面全作废 -
WHERE a IS NULL OR b = 1更不行 ——OR会让优化器放弃复杂路径
UNIQUE 索引 + IS NULL 是性能黑洞
SQL 标准规定 NULL != NULL,所以 UNIQUE 索引允许无限多个 NULL。这导致两个后果:
优化器无法假设 “IS NULL 最多返回 1 行”,丧失快速终止依据;统计信息中该列的 Cardinality 通常严重失真,进一步干扰成本估算。执行 SHOW INDEX FROM t 时如果看到 Null 列为 YES,又带 Non_unique=0(即 UNIQUE),就要警惕这个组合。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
实测中,哪怕只有 5% 行满足 col IS NULL,在 UNIQUE 索引上也可能触发全表扫描,而普通二级索引同样条件下大概率走索引。
验证和绕过的唯一可靠方式是 EXPLAIN 对比
别猜,用真实数据跑计划对比:
- 先跑
EXPLAIN SELECT * FROM t WHERE c IS NULL,记下key和rows - 再跑
EXPLAIN SELECT * FROM t FORCE INDEX (idx_c) WHERE c IS NULL,看rows是否显著下降 - 如果强制后变快,说明优化器误判;如果没变化,基本是
NULL比例过高或索引设计不合理
终极绕法不是调优,而是业务层改逻辑:把 IS NULL 替换成非空标记,比如建函数索引 INDEX idx_coalesce (COALESCE(c, '_NULL_')),并统一用 COALESCE(c, '_NULL_') = '_NULL_' 查询。MySQL 8.0.13+ 对这类函数索引支持良好,但需要应用层配合改造。










