NOT NULL 使索引真正可用:B+树不存储NULL,避免优化器弃用索引、IS NULL全表扫描、复合索引范围查找失效,并减少存储开销与统计干扰。

NOT NULL 让索引真正“可用”
MySQL 的 B+ 树索引不存储 NULL 值——不是跳过,是压根不写进去。这意味着 WHERE col = ? 查询在 col 允许 NULL 时,优化器可能直接弃用索引,尤其当统计信息显示该列 NULL 占比高(哪怕只有 5%)。而一旦加了 NOT NULL,优化器就知道“所有值都可比较”,敢用索引做等值查找,EXPLAIN 中的 key 字段才会稳定命中索引。
IS NULL 查询强制全表扫描
只要列允许 NULL,WHERE col IS NULL 就无法走索引,只能全表扫描。这不是 bug,是 InnoDB 的设计:B+ 树里没有 NULL 的位置。而如果你本就不需要 NULL,却留着这个可能性,等于主动给查询埋了个雷。线上慢查日志里反复出现 type: ALL + rows: 数百万,十有八九是某个关联字段或过滤字段没设 NOT NULL。
复合索引中 NULL 削弱范围查找能力
比如复合索引 (a, b),若 b 允许 NULL,那么 WHERE a = 1 AND b > 10 很可能无法利用 b 的有序性做范围截断。因为 NULL 在 B+ 树中被特殊处理(通常排在最左或最右),导致优化器不敢假设 b 的值连续分布。结果就是只用上 a,b > 10 变成回表后过滤,key_len 明显偏小。
NOT NULL 减少存储开销与统计干扰
InnoDB 为每个可为 NULL 的列分配 1 bit(按字节对齐),1000 万行表就多占约 1.2MB;全 NOT NULL 则完全省掉。更重要的是,NULL 值会污染列的统计信息(如 cardinality),让优化器误判数据分布,进而选错执行计划。而 NOT NULL + DEFAULT '' 不仅省空间,还让字段语义确定——空字符串是值,NULL 是未知,二者在索引、比较、JOIN 中行为完全不同。
真正容易被忽略的点是:NOT NULL 本身不加速查询,它只是让索引和优化器“敢放手干活”。很多团队补索引后性能没起色,回头一看,字段还挂着 NULL 允许,等于给引擎戴了镣铐还让它跑百米。











