外键仅InnoDB支持,MyISAM忽略FOREIGN KEY语法且不报错;建表需显式指定ENGINE=InnoDB,已有表用ALTER TABLE转换;ON DELETE/UPDATE未声明默认RESTRICT;字段类型、字符集、排序规则须严格一致;删外键不删索引,需手动清理。

外键必须用 InnoDB,MyISAM 不支持
直接执行 FOREIGN KEY 语句却报错 ERROR 1005 (HY000): Can't create table?大概率是表引擎不对。MySQL 中只有 InnoDB 存储引擎支持外键约束,MyISAM 完全忽略 FOREIGN KEY 语法,也不报错,但实际没生效。
建表时显式指定引擎:
CREATE TABLE emp ( id INT PRIMARY KEY, dept_id INT, CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id) REFERENCES dept(id) ) ENGINE=InnoDB;
已有表需先转换引擎:ALTER TABLE emp ENGINE=InnoDB;
- 转换前确认目标表无外键依赖,否则会失败
- 转换过程会锁表,大表慎操作
-
SHOW CREATE TABLE emp;可验证当前引擎和外键定义
ON DELETE 和 ON UPDATE 必须显式声明,不写就是 RESTRICT
很多人以为不写 ON DELETE 就“默认不管”,其实 MySQL 默认行为是 RESTRICT:只要子表有匹配记录,父表的 DELETE 或 UPDATE 就直接报错,比如 Cannot delete or update a parent row: a foreign key constraint fails。
常见组合及适用场景:
-
ON DELETE CASCADE:适合强生命周期绑定,如订单明细随订单删除;但误删风险高,生产环境需谨慎 -
ON DELETE SET NULL:要求外键列允许NULL(即不能带NOT NULL),适合“解绑但保留记录”,如员工调离部门后部门 ID 置空 -
ON UPDATE CASCADE:极少用——主键本不该频繁更新;若真要改,说明设计有问题(比如用业务码当主键) -
ON UPDATE SET NULL在 InnoDB 中语法合法但几乎无实际意义,不推荐
添加带级联的外键示例:
ALTER TABLE emp ADD CONSTRAINT fk_emp_dept_id FOREIGN KEY (dept_id) REFERENCES dept(id) ON DELETE SET NULL ON UPDATE RESTRICT;
外键列和被引用列的数据类型、长度、符号必须严格一致
哪怕只是 TINYINT 和 SMALLINT、INT(10) 和 INT(11)、INT UNSIGNED 和 INT,都会导致 ERROR 1005 或建表静默失败。
检查要点:
- 用
DESCRIBE dept;和DESCRIBE emp;对比字段类型 - 主表主键是
INT UNSIGNED,子表外键也必须是INT UNSIGNED - 字符集和排序规则也要一致,尤其是涉及
VARCHAR外键时(虽不推荐) - 复合外键时,列顺序、数量、类型必须与主表主键完全对应
修复方法通常是重建子表字段:ALTER TABLE emp MODIFY dept_id INT UNSIGNED;
删除外键后,关联索引不会自动清除
MySQL 在创建外键时会自动为外键列创建索引(名称通常类似 fk_emp_dept_id),但执行 ALTER TABLE emp DROP FOREIGN KEY fk_emp_dept_id; 只删约束,不删索引。
后果:
- 冗余索引占用空间、拖慢写入性能
- 后续再加同名外键会因索引冲突失败
-
SHOW INDEX FROM emp;能看到残留索引
手动清理方式:
DROP INDEX fk_emp_dept_id ON emp;
注意:如果该列上已有业务需要的其他用途索引(比如用于查询),别误删。
外键不是银弹,级联行为一旦上线就很难回退——尤其是CASCADE 类型,线上执行前务必在备份库验证逻辑,且避免在高频更新的大表上启用 ON UPDATE CASCADE。


















