mysql唯一索引允许多个null值,是遵循sql标准的设计行为而非bug;因null表示“未知”,null= null结果为unknown,故不触发唯一性校验,单列或联合索引中任一列为null即整行绕过判重。

允许,而且是设计使然,不是 bug。MySQL(InnoDB)对唯一索引中 NULL 的处理完全遵循 SQL 标准:NULL 表示“未知”,NULL = NULL 的结果是 UNKNOWN,而非 TRUE,因此不触发唯一性冲突。
单列唯一索引插入多个 NULL 为什么会成功?
因为 InnoDB 在唯一性检查时,直接跳过值为 NULL 的列——既不拿它跟别的 NULL 比,也不拿它跟非 NULL 值比。
-
INSERT INTO users (email) VALUES (NULL);执行 10 次,全部成功 - 只要出现两条
email = 'a@b.com',立刻报错:ERROR 1062 (23000): Duplicate entry 'a@b.com' for key 'uk_email' - 这说明约束只作用于非
NULL值,NULL是“免检区”
联合唯一索引里一个 NULL 就让整行“隐身”
比如索引定义为 UNIQUE KEY (category_id, unit_id, model_hash),只要任意一列是 NULL,该行就绕过整组判重逻辑。
-
(1, 2, NULL)和(1, 2, NULL)可同时存在 -
(NULL, 100, 'abc')和(NULL, 100, 'abc')也能插入成功 -
(NULL, NULL, NULL)插入十次都不会报错 - 这不是索引失效,是标准行为:任一列为
NULL→ 整行不参与唯一性校验
INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 对 NULL 无效
这两个语法只在真正触发唯一冲突时起作用;而 NULL 导致的“伪重复”根本不会进入冲突检测路径,所以它们完全不生效。
-
INSERT IGNORE INTO t VALUES (1, NULL);执行两次,都成功,无提示、无更新 -
INSERT ... ON DUPLICATE KEY UPDATE同样跳过UPDATE分支 - 别指望靠语法兜底——问题不在写法,而在数据语义:你写了
NULL,MySQL 就当它不存在于约束范围内
想让 NULL 也受唯一约束?只能堵,不能疏
MySQL 没有配置项能让唯一索引把 NULL 当普通值对待。可行路径只有从源头消除 NULL 的语义歧义:
- 建表时加
NOT NULL+ 默认值,例如status TINYINT NOT NULL DEFAULT -1 - 用生成列兜底:
mobile_key VARCHAR(20) STORED AS (COALESCE(mobile, '__MISSING__')),再在mobile_key上建唯一索引 - 应用层统一转换:ORM 或服务代码里把空值转成哨兵字符串(如
'__NULL__'),禁止写入原始NULL
最容易被忽略的是:你看到 SHOW INDEX FROM t 显示索引存在,就默认它“管用”;但只要字段允许 NULL,它就在关键业务场景下静默失效——校验必须落到每一列的 NULL 属性上,而不是只看索引定义本身。











