外键必须建在InnoDB引擎表上,MyISAM等不支持;主表字段须为主键或唯一索引;外键列与被引用列数据类型、符号、长度、排序规则须完全一致;跨库引用不被允许;ON DELETE/UPDATE行为需按业务谨慎选择。

外键必须建在InnoDB引擎表上
MyISAM等引擎完全不支持外键,即使语法通过也不会生效。执行 SHOW CREATE TABLE 查看建表语句时,如果没看到 FOREIGN KEY 定义,或者提示 ENGINE=MyISAM,那外键就形同虚设。
建表时显式指定引擎是最稳妥的做法:
CREATE TABLE orders ( id INT PRIMARY KEY, user_id INT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 已有 MyISAM 表不能直接加外键,得先
ALTER TABLE table_name ENGINE=InnoDB - 跨库引用(如
db1.users→db2.orders)在 MySQL 中不被允许,外键只能指向同一数据库内的表
主表字段必须是主键或唯一索引
外键指向的列(比如 users.id)必须有 PRIMARY KEY 或 UNIQUE 约束,否则会报错 ERROR 1005 (HY000): Can't create table 或更具体的 errno: 150。
常见误操作是只加了 INDEX 而没加 UNIQUE,例如:
-- ❌ 错误:仅普通索引,无法作为外键目标 CREATE INDEX idx_email ON users(email);
-- ✅ 正确:唯一索引才可被引用 CREATE UNIQUE INDEX uk_email ON users(email);
- 复合主键也可作为外键目标,但子表外键字段顺序和数量必须严格一致
-
NOT NULL不是强制要求(外键列本身可以为 NULL),但被引用列必须非空(主键天然满足)
数据类型和属性必须完全一致
这是最常踩的坑:user_id INT 指向 users.id TINYINT、INT UNSIGNED 指向 INT SIGNED、甚至 INT(10) 和 INT(11) 都会导致 ERROR 1215: Cannot add foreign key constraint。
检查方式很简单:
SHOW COLUMNS FROM users LIKE 'id';
SHOW COLUMNS FROM orders LIKE 'user_id';
- 必须完全匹配:类型(
INT/BIGINT)、符号(SIGNED/UNSIGNED)、长度(如INT(10) UNSIGNED对INT(10) UNSIGNED) - 字符类型还要注意排序规则(
COLLATION),utf8mb4_0900_as_cs和utf8mb4_general_ci不兼容 -
ENUM和SET类型不能作外键目标(MySQL 不支持)
ON DELETE / ON UPDATE 的行为选择直接影响业务逻辑
级联动作不是“锦上添花”,而是业务规则的直接编码。选错会导致静默删数据或阻断关键操作。
典型组合与适用场景:
-
ON DELETE RESTRICT(默认):订单存在时禁止删用户 → 适合强一致性核心实体,避免孤儿记录 -
ON DELETE CASCADE:删用户则自动删其全部订单 → 适合临时账号、测试数据清理,但生产环境慎用 -
ON DELETE SET NULL:删用户后订单保留,user_id设为NULL→ 要求该字段允许NULL,且业务能容忍“无主订单” -
ON UPDATE CASCADE极少使用:主键被更新(如 UUID 改 ID)时同步更新子表 → 实际中主键应 immutable,尽量避免
一个容易被忽略的点:RESTRICT 和 NO ACTION 在 InnoDB 中行为相同,但语义不同——前者是立即检查,后者可能延迟到事务提交时,不过实践中无需区分。
真正复杂的点在于:外键约束本身不解决“软删除”“归档迁移”这类业务需求,它只管物理删除/更新。如果业务需要标记删除而非真实删除,外键就帮不上忙,得靠应用层或触发器兜底。


















