首页/数据/SQL 速查/NULL 值与索引使用
性能与执行计划72 条

NULL 值与索引使用

IS NULL 通常可走索引;负向条件(<>、NOT IN、NOT LIKE)容易退回扫描,能否走索引以实际计划为准。

SQL 速查数据更新于 2026-10-13

NULL 与索引的关系

NULL 与索引的关系常被误解。B 树索引会为 NULL 建立条目,所以 IS NULL 通常可以走索引;真正难走索引的是负向条件,比如不等于、NOT IN、NOT LIKE,它们在多数情况下会退化成扫描。能否命中以实际执行计划为准。

基本信息

NULL 的存储InnoDB 与 PostgreSQL 的 B 树都会为 NULL 建立条目
IS NULL一般可以走索引
负向条件不等于、NOT IN、NOT LIKE 多数情况下无法走索引
唯一索引两库默认允许多个 NULL 共存
验证方式以 EXPLAIN 输出为准

典型使用场景

  • 查询未被逻辑删除的记录
  • 排查「明明有索引却走了全表扫描」
  • 把负向条件改写成正向条件
  • 筛选某字段为空的数据
  • 做容量评估时把 NULL 条目算进去

示例

示例
-- 可走索引:B 树包含 NULL 条目
SELECT * FROM users WHERE deleted_at IS NULL;

-- 负向条件:多数情况退化为扫描
SELECT * FROM users WHERE status <> 'active';
SELECT * FROM users WHERE email NOT LIKE 'a%';

-- 改写思路:用状态枚举的正向条件替代负向条件
SELECT * FROM users WHERE status IN ('pending', 'locked');

条件形态与索引

条件形态通常能否走索引说明
col IS NULL通常可以B 树包含 NULL 条目
col IS NOT NULL视选择性而定命中大部分行时不划算
col <> 值多数不能除非命中比例极低且统计支持
NOT IN多数不能还叠加 NULL 语义陷阱
NOT LIKE '前缀%'不能无法利用前缀有序性
NOT IN 有两个问题叠加:索引通常用不上,子查询里含 NULL 时结果还可能整段为空。改写成 NOT EXISTS 或正向枚举,往往一次解决两点。
NULL 会占索引空间:存在 NULL 的列,索引体积与选择性都会受影响,做容量评估时别忽略这部分。
统计信息里 NULL 的占比会影响优化器判断:NULL 很多时 IS NULL 的选择性变差,优化器可能放弃索引改走全表扫描,这和索引本身无关。
把「未知」和「空值」混在同一列里会让索引与查询都变含糊:状态与业务字段尽量用明确取值表达,NULL 只保留一个含义。

易错点

  • 把 NOT IN 当普通条件优化:既有索引问题又有 NULL 语义陷阱,优先改写语句
  • 认为 NULL 不占索引空间:B 树会记录 NULL 条目,索引体积与选择性都受影响
  • 以为 IS NOT NULL 一定能加速:命中行占大多数时优化器宁可全扫,收益取决于选择性
  • 用负向条件后又加索引期待提速:加了索引也大概率用不上,应先改写查询
  • 忽略 NULL 对唯一性的影响:唯一索引默认允许重复 NULL,需要严格唯一就配合 NOT NULL

数据来源与参考

  • MySQL 8.0 Reference Manual §8.3.1 How MySQL Uses Indexes
  • PostgreSQL Documentation §11.2 Index Types
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌