mysql不推荐默认值为null,因其在where条件(=null恒为unknown)、unique索引(允许多个null)、count统计(自动忽略)、索引存储(额外空值位)、update行为(静默跳过)等方面引发语义混乱与性能隐患,应改用not null配合显式默认值。

MySQL 允许字段默认值为 NULL,但实际建表时只要没写 NOT NULL,就自动获得 NULL 约束——这看似省事,却埋下多个隐性故障点。不推荐用 NULL 作默认值,核心原因不是“索引完全失效”,而是它在语义、存储、逻辑和约束层面都引入了不可控变量。
WHERE 条件里 = NULL 永远查不到数据
这是最常踩的坑:开发者直觉认为 WHERE status = NULL 能查出空状态记录,但 MySQL 中任何与 NULL 的等值比较(=、!=、IN、NOT IN)结果都是 UNKNOWN,而 WHERE 只保留 TRUE 行。
- 必须写成
WHERE status IS NULL才能命中 -
NOT IN (SELECT user_id FROM orders):只要子查询里任意user_id是NULL,整个条件恒为FALSE,结果集为空 -
COUNT(status)自动跳过所有NULL,容易误判字段填充率
UNIQUE 索引对 NULL 形同虚设
MySQL 规定:UNIQUE 约束允许无限多个 NULL 值,因为 NULL ≠ NULL。这意味着你建了个“邮箱唯一”索引,却可以插入 100 条 email = NULL 的用户记录。
- 业务上想保证“非空邮箱唯一”,但字段允许
NULL就等于开了后门 - 修复方式只能是加
NOT NULL+ 显式默认值(如DEFAULT ''),再配合应用层校验 - 已有数据需先清理
NULL,否则ALTER TABLE ... MODIFY email VARCHAR(255) NOT NULL会失败
索引存储开销和 key_len 异常增大
InnoDB 对每个可空列,在每条索引记录中额外分配 1 字节的“空值标记位”。这不只是多占空间,更影响 B+Tree 效率:
-
EXPLAIN中key_len比预期大 1(例如VARCHAR(10)本应 31,加NULL后变 32) - 单页索引记录数减少 → B+Tree 层数可能增加 → 随机 I/O 次数上升
- 联合索引中任一列为
NULL,优化器对范围扫描更保守,部分谓词可能无法下推 - MyISAM 更敏感:原本固定长度的
INT索引因允许NULL被迫转为可变长,引发碎片
UPDATE 语句可能“静默跳过”更新
当字段当前值是 NULL,你执行 UPDATE t SET col = NULL WHERE id = 1,MySQL 优化器会判定“无变更”,直接跳过物理更新。
- 依赖
ON UPDATE CURRENT_TIMESTAMP的时间戳字段不会刷新 - 触发器(
BEFORE UPDATE)不会触发 - binlog 中不记录该行变更,CDC 工具可能漏掉这条逻辑更新
- 现象是
Rows matched: 1 Changed: 0,但业务以为时间已更新
真正麻烦的不是“能不能用 NULL”,而是它让同一字段在不同上下文里行为不一致:有时被忽略(COUNT),有时被特殊对待(UNIQUE),有时彻底失语(WHERE)。与其在每个 SQL、每个应用层补丁去防御,不如建表时就用 NOT NULL + 合理默认值堵死源头——哪怕只是 DEFAULT '' 或 DEFAULT 0,也比放任 NULL 更可控。











