外键是InnoDB强制的引用检查机制,要求字段类型严格兼容、显式建索引,支持ON DELETE/UPDATE行为控制,非银弹,需权衡一致性、性能与架构演进。
外键不是语法糖,也不是可有可无的装饰——它是 MySQL(仅 InnoDB)对“某条记录是否真的存在另一张表里”做的实时校验。比如 orders.user_id 设为外键指向 users.id,那插入 orders 时若填了个不存在的 user_id,MySQL 直接报错:Cannot add or update a child row: a foreign key constraint fails。这不是靠程序逻辑兜底,而是数据库层的硬性拦截。
InnoDB 表生效,MyISAM 完全无视外键定义(即使你写了也不会报错,但也不起作用)ALTER TABLE orders ADD INDEX idx_user_id (user_id)),否则建外键会失败 —— 即使字段名和主键一样,也不会自动索引INT 和 TINYINT UNSIGNED 不行,BIGINT 和 INT 也不行;字符集、排序规则也得一致外键不加 ON DELETE 或 ON UPDATE,就等于只开了“禁止非法插入”,但没管“父记录变了怎么办”。常见取值有:
RESTRICT(默认):删/改父记录前,先查子表有没有引用,有就直接拒绝CASCADE:父删,子自动删;父改主键值,子外键值跟着改(慎用!尤其改主键在生产环境几乎从不发生)SET NULL:要求外键列允许 NULL;父删/改后,子表对应外键字段设为 NULL
NO ACTION:和 RESTRICT 在 MySQL 中行为一致,语义上更偏向“由应用决定”,但实际仍是拒绝举个真实场景:用户注销时想保留订单历史但断开归属,应设 ON DELETE SET NULL;而删除产品时连带清空库存记录,才用 CASCADE。别图省事全写 CASCADE,一个误删可能级联干掉几十张表的数据。
启用外键意味着每次 INSERT/UPDATE/DELETE 都要多一次关联表的索引查找和锁检查。高并发写入场景下,外键约束可能成为瓶颈,尤其是跨分片或大表 JOIN 的外键。
SET FOREIGN_KEY_CHECKS = 0,导入完再开(但务必确认数据逻辑自洽)ALTER TABLE orders ADD CONSTRAINT fk_orders_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ON UPDATE RESTRICT;
主键一定是唯一 + 非空 + 自动建聚簇索引;外键只是普通字段,它本身不保证唯一、不强制非空(除非你额外加 NOT NULL),且必须手动建索引。很多人以为“加了外键就自动索引了”,结果上线后 JOIN 慢得离谱,explain 一看 type: ALL —— 就是因为忘了给外键列加索引。
orders.user_id)UNIQUE 约束