外键约束在InnoDB中需显式启用且字段类型、索引等必须严格匹配,否则无效;级联操作具事务性并可能引发锁争用;禁用检查易致数据不一致,不可替代应用层校验。
MySQL 默认启动时并不强制所有表使用外键约束,即使你用了 FOREIGN KEY 语法建表,InnoDB 也可能不实际执行检查——前提是 FOREIGN_KEY_CHECKS 被设为 0 或者建表时没启用严格模式。
常见错误现象:插入子表记录时没报错,但父记录根本不存在;或者删了父记录,子记录却还留着,看起来“外键没起作用”。
SELECT @@FOREIGN_KEY_CHECKS;
SET FOREIGN_KEY_CHECKS = 1;
my.cnf 中添加:innodb_force_foreign_keys = ON(MySQL 5.6.16+ 支持)
CREATE TABLE orders ( id INT PRIMARY KEY, user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ) ENGINE=InnoDB;
InnoDB 的外键级联动作(如 ON DELETE CASCADE)不是“自动拼 SQL”,而是由存储引擎在事务内部统一调度。这意味着一次 DELETE 可能引发多张表的行变更,并全部包裹在同一个事务中。
容易踩的坑:以为 CASCADE 是“事后清理”,结果发现它也受锁机制影响,甚至可能造成锁等待或死锁。
ON UPDATE CASCADE 和 ON DELETE CASCADE 都会在父表操作时,同步更新/删除子表匹配行,且不可回滚单个子表动作CASCADE 可能长时间持有 FOR UPDATE 锁,阻塞其他读写CASCADE 时,DELETE FROM parent 会直接报错:Cannot delete or update a parent row: a foreign key constraint fails
SHOW ENGINE INNODB STATUS\G 查看最近死锁时,常能看到外键触发的锁等待链InnoDB 要求外键列和被引用列不仅逻辑语义相同,连数据类型、字符集、排序规则、是否允许 NULL 都得完全匹配,否则建表失败或约束无效。
典型报错:
ERROR 1005 (HY000): Can't create table `db`.`t2` (errno: 150 "Foreign key constraint is incorrectly formed")
user_id INT 不能引用 id BIGINT,哪怕值域重叠也不行utf8mb4_unicode_ci,子表外键也得是同一 collationINT UNSIGNED,子表外键也必须声明 UNSIGNED,否则报错有些迁移脚本会用 SET FOREIGN_KEY_CHECKS = 0 临时绕过约束,再批量导入数据。这本身没问题,但极易留下数据一致性隐患。
关键问题不在“能不能关”,而在于“关了之后有没有人记得开、有没有人验证数据是否真合规”。
0 状态,后续 DML 意外跳过检查mysqldump --skip-foreign-key-checks 导出时,恢复前务必确认目标库 FOREIGN_KEY_CHECKS 为 1,否则 dump 文件里的 SET FOREIGN_KEY_CHECKS=0 会生效SELECT id FROM parent WHERE id = ? 再插入子表