MySQL笔记·第2篇:MySQL外键——阿里巴巴为什么禁止使用?外键的坑与替代方案
一、外键是什么?

简单说,外键就是用来保证“两张表的数据能对上”。比如订单表的user_id必须指向用户表里存在的id。
1 | FOREIGN KEY (user_id) REFERENCES user(id) |
加了外键,数据库会自动帮你检查:插入订单时,用户必须存在;删除用户时,要么拒绝,要么连带删订单。
听起来很完美,对吧?那为什么阿里巴巴开发手册明确写着“不得使用外键”?这篇文章讲清楚外键的坑和替代方案
二、为什么不推荐用外键?

外键像个负责任的保安,每次有人进出都查证件——安全是安全了,但门口排起了长队。
1. 性能损耗
每次插入、更新、删除订单,数据库都要去user表查一下这个user_id存不存在。这条select语句看着简单,但在高并发下,数据库累死。
一个订单操作变成:插入订单 + 查询用户。数据库要多干一份活。
2. 分库分表不友好
外键只能在一个数据库实例内生效。
业务大到一定程度,user表在一台机器,orders表在另一台机器。外键直接废了,代码还得大改——把所有数据完整性检查从数据库搬到代码里。
与其到时候哭着改代码,不如一开始就别用外键。
3. 耦合太紧
想删user表的一条数据?先看看有没有订单引用它。想改表结构?先拆外键。想迁移数据?外键关系像蜘蛛网一样缠着。
一个表的变动,牵一发动全身。
4. 开发测试麻烦
造测试数据的时候,得先插user,再插orders,顺序不能乱。清数据的时候,得先删orders,再删user,顺序也不能乱。
每次测试都得小心翼翼地按顺序来,很烦。
三、不用外键,怎么保证数据完整性?

大厂的做法是:不设保安,装一套门禁系统。
| 方案 | 做法 |
|---|---|
| 应用层保证 | 在代码里写逻辑:插入订单前,先查一下用户存不存在。 |
| 事务 | 用数据库事务保证一系列操作“要么全成功,要么全失败”,防止中间状态不一致。 |
| 唯一索引/检查约束 | 用这些轻量级的约束代替外键,成本低很多。 |
| 定期巡检 | 跑定时任务,扫出“孤儿数据”(比如查无此人的订单),修掉或报警。 |
1 | // 应用层保证示例 |
代码多写几行,但数据库轻松了,系统也能水平扩展了。
四、什么时候还可以用外键?

| 场景 | 判断 |
|---|---|
| 小项目、并发低 | 可以用。外键的便利性大于性能损失。 |
| 单机部署、不拆分 | 可以用。没有分库分表的烦恼。 |
| 财务系统、对账系统 | 可以用。数据一致性要求极高,宁愿慢也不能错。 |
| 内部管理系统 | 可以用。并发不高,外键省事。 |
阿里那个规范说的是“不得使用”,那是针对他们那种千万级并发的系统。如果需要维护的是小项目,则可以无视这些
五、总结
| 方案 | 优点 | 缺点 |
|---|---|---|
| 用外键 | 省事、数据库自动保证完整性 | 性能差、分库分表难、耦合紧 |
| 不用外键 | 性能好、扩展灵活、解耦 | 代码多写几行、需要自己保证完整性 |
外键像个负责任的保安,安全但效率低。大厂不要保安,而是装门禁系统,每个人自己刷卡,系统只记录谁没刷。安全靠制度和技术,不靠一个人堵在门口。
不是外键不好,是它的代价超出了它的价值。








