外键只在innodb引擎且字段类型严格一致时生效;联合外键须按序完整匹配主表索引;外键名需库内唯一;cascade慎用,应用层控制更安全。

外键没生效?检查 ENGINE=InnoDB 和字段类型是否严格一致
MySQL 中外键只在 InnoDB 引擎下生效,MyISAM 表加了 FOREIGN KEY 语法也不会报错,但实际不校验、不级联、不阻止插入非法值。这是最常被忽略的前提。
字段类型也必须完全一致:比如主表 id 是 INT UNSIGNED,从表的外键字段也得是 INT UNSIGNED,哪怕只是少个 UNSIGNED,建约束就会失败或静默失效。
-
SHOW CREATE TABLE查看两张表的实际引擎和字段定义 - 建表时显式写
ENGINE=InnoDB,别依赖默认配置 - 用
ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4类操作可能隐式改字段属性,连带破坏外键兼容性
ON DELETE CASCADE 不是万能的,循环依赖和大表删除会卡死
设了 ON DELETE CASCADE 后,删主表一行可能触发链式删除——比如删一个用户,连带删订单、订单项、物流记录……看似省事,实则风险集中。
两个常见坑:DELETE FROM users WHERE id = 123 可能锁住几十张表;如果 A 表外键指向 B,B 又外键指向 A(哪怕间接),MySQL 直接拒绝建约束,但开发中容易在中间表绕开检查,运行时才爆 Cannot delete or update a parent row: a foreign key constraint fails。
- 生产环境慎用
CASCADE,优先用应用层分步删除 + 事务控制 - 用
SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 'users'扫描级联路径 - 大表关联删除前先加
LIMIT测试,避免误删+锁表
联合外键必须按顺序匹配主键/唯一索引字段
如果主表主键是 (org_id, user_id),从表想引用它,外键字段必须也是两个、同序、同类型,且主表上该组合必须有唯一索引(主键自动满足)。顺序错一位,比如写成 (user_id, org_id),MySQL 不报错,但约束不生效。
更隐蔽的是:主表有唯一索引 UNIQUE KEY idx_org_user (org_id, user_id),但从表外键只引用了 org_id 单字段——这不合法,因为外键必须完整匹配索引的最左前缀。
- 建联合外键前,先确认主表对应索引的字段顺序和数量
- 用
SHOW INDEX FROM parent_table核对索引列顺序 - 不要指望优化器“猜”你意图,字段顺序差一个,就等于没约束
外键名重复会导致 ALTER TABLE ADD CONSTRAINT 失败
MySQL 要求外键名在**整个数据库内唯一**,不是单表内唯一。很多人在不同表里都用 fk_user_id,第二次加就会报 ERROR 1022 (HY000): Can't write; duplicate key in table。
名字还影响错误提示可读性:Cannot add or update a child row: a foreign key constraint fails 后面不显示具体是哪个约束,只能靠名字定位。名字太泛,查问题就得翻建表语句。
- 命名建议格式:
fk_从表名_主表名_字段名,例如fk_order_user_user_id - 加约束前先查
SELECT CONSTRAINT_NAME FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA = 'db_name' - 线上加约束失败时,别急着重试,先
SHOW ENGINE INNODB STATUS看最近的外键冲突详情
外键真正难的不是语法,是理解它把数据一致性从应用层推给了存储引擎——而引擎只管“当时”,不管“之后”。比如主表某行被更新后,从表旧数据是否还合法?外键不校验。这类逻辑还得靠业务代码兜底。










