惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
Y
Y Combinator Blog
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
S
SegmentFault 最新的问题
IT之家
IT之家
Recent Announcements
Recent Announcements
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
T
The Blog of Author Tim Ferriss
Martin Fowler
Martin Fowler
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
U
Unit 42
WordPress大学
WordPress大学
博客园 - Franky
L
LangChain Blog
人人都是产品经理
人人都是产品经理
小众软件
小众软件
博客园 - 叶小钗
罗磊的独立博客
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
雷峰网
雷峰网
腾讯CDC
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Help Net Security
Help Net Security
C
Check Point Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Schneier on Security
Schneier on Security
博客园 - 聂微东
A
Arctic Wolf
H
Heimdal Security Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
The Exploit Database - CXSecurity.com
C
Cyber Attacks, Cyber Crime and Cyber Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Google DeepMind News
Google DeepMind News

博客园 - 叫我安不理

C#如何优雅处理引用类型的深拷贝 字符编码知多少(二) 字符编码知多少(一) 给自己看的mongoDB快速入门 为什么OAuth2与SSO经常混为一谈? 优雅求模,一致性哈希算法 浅谈为什么我讨厌分布式事务 为什么说方法的参数最好不要超过4个? C#代码如何影响CPU缓存速度? 浅谈.NET微服务架构的演变 C#网络编程(六)----Socket编程模型 C#网络编程(五)----细嗦TCP粘包 C#网络编程(四)----HttpClient C#网络编程(三)----HTTP协议 C#网络编程(二)----网络层/链路层 C#网络编程(一)----应用层/传输层 重生之我是操作系统(十)----I/O管理 重生之我是操作系统(九)----文件管理(下) 重生之我是操作系统(八)----文件管理(上) 重生之我是操作系统(七)----内存管理(下)
老马失前蹄,竟然在数据库外键上翻车了,重温外键级联
叫我安不理 · 2026-03-22 · via 博客园 - 叫我安不理

祸起 dev environment

某天晚上,群里突然有人@我,说开发环境出现报错,一查发现是我负责的接口出了问题,瞬间虎躯一震,赶紧定位问题。

image

谜底就在谜面上:主表数据被删除,但子表仍保留外键约束,导致删除操作直接失败

三分钟解决问题,行云流水,毫不拖泥带水。


反思与复盘

但这件事引发了我的反思:这种错误太低级了,低级到刚毕业的我都不可能犯,但工作多年后的我反而踩坑了。

复盘原因,主要有三点:

  1. 对外键本质理解不够深入
    人云亦云、浅尝辄止,没有真正吃透原理。
    温故而知新,希望未来不再被同样的问题“回旋镖”命中。

  2. 过度依赖 DBA 兜底,思考偷懒
    经验主义害人,任何技术点都不能“不带脑子”。

  3. 对业务上游逻辑不够熟悉
    不知道上游业务会执行删除操作。
    后续需要加强业务文档沉淀、多总结、多梳理。


外键 Foreign Key 解析

什么是外键?

数据库外键,是一个熟悉又陌生的词。

  • 熟悉:大学就学过,工作天天提
  • 陌生:商业项目里几乎从不真正启用

至于为什么不建外键?别问,问就是互联网公司传统!
当然,主要是没权限。
那为什么现在又开始建了?
一代版本一代神,换工作了 😂

外键(Foreign Key) 是一种数据约束,用于强制维护两张表之间的关联关系

通俗比喻

  • 主表 = 身份证表
  • 子表 = 学生表
  • 外键 = 规则约束:学生表中的身份证号,必须在身份证表中存在

核心作用:保证数据不乱、不丢、不错


外键能解决什么问题?

没有外键,很容易出现三类脏数据:

  1. 子表存在不存在于主表的引用值
  2. 主表数据被删,子表残留“孤儿数据”
  3. 关联查询逻辑混乱,需要频繁调整 JOIN 方式

有了外键:

  1. 数据库自动校验,无法非法插入/删除
  2. 关联关系永远合法
  3. 支持自动级联操作

外键常用 SQL 语法

1. 建表时直接添加外键

CREATE TABLE 子表 (
    字段名 数据类型,
    -- 外键定义
    CONSTRAINT 外键名称 
    FOREIGN KEY (子表字段) 
    REFERENCES 主表(主表字段)
    -- 可选:级联规则
    ON DELETE 行为
    ON UPDATE 行为
);

2. 给已有表添加外键

ALTER TABLE 子表
ADD CONSTRAINT 外键名称
FOREIGN KEY (子表字段)
REFERENCES 主表(主表字段)
ON DELETE 行为
ON UPDATE 行为;

3. 删除外键

ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

4. 修改外键(MySQL 不支持直接修改,需先删再加)

其他数据库如 SQL Server:

ALTER TABLE 表
ALTER CONSTRAINT 外键名
ON DELETE CASCADE;

⚠️ 最重要:外键的四种级联行为

外键的灵魂就是:删除/更新主表时,子表应该如何自动处理

1. RESTRICT(默认行为,最安全)

  • 如果子表存在引用,直接报错,阻止操作
  • 不允许删除/更新主表记录

2. CASCADE(级联,最危险)

  • 主表删除 → 子表自动删除
  • 主表更新 ID → 子表自动同步
  • 一旦误操作,极易批量丢失数据

3. SET NULL

  • 主表删/改 → 子表对应字段自动设为 NULL
  • 要求子表字段允许为 NULL

4. SET DEFAULT(极少使用)

  • 与 SET NULL 类似,只是设为预设默认值

image


企业最常用的安全组合

ON DELETE RESTRICT  -- 不允许删除
ON UPDATE CASCADE   -- 允许改 ID,自动同步

为什么大多数互联网公司不使用外键?

外键优点

  1. 数据强一致性,数据库底层保证数据合法性
  2. 无需在代码中手动校验关联关系,简化逻辑
  3. 表关系清晰,便于生成 E-R 图、维护表结构

外键缺点(核心原因)

  1. 降低写入性能
    插入/删除都需要额外校验,高并发场景下影响明显。

  2. 隐式加锁,易导致锁等待、死锁
    例如插入子表时,数据库会自动查询并锁定主表对应记录,提高死锁概率。

  3. 不支持分库分表
    外键是数据库引擎内部功能,只能在同一个库、同一个实例内生效。
    分布式架构下完全无法使用。

  4. 级联删除风险极高
    误操作难以恢复,不符合互联网业务“可容错、可兜底”的需求。


总结一句话

互联网企业不是不会用外键,而是不敢用、不能用、不需要用。
业务更需要:逻辑删除、数据迁移、临时异常容忍、灵活清洁数据。
如果你还是听不懂,那说人话就是,我们言必谈高并发,分布式,高可用,只有你坚持古法单数据库,强一致性,活该你背锅。
image


常见问题

1. 外键名称必须全库唯一吗?

是的,外键名称不能重复。

2. 子表已有数据,还能加外键吗?

只要现有数据完全合法(都能在主表找到对应记录),就可以添加;否则会直接失败。

3. 外键引用的字段必须是主键吗?

不需要主键,但必须具备唯一性
唯一键(UNIQUE)也可以被外键引用。

4. 为什么外键容易产生锁等待,死锁

举个例子,当你执行这条SQL时,因为外键约束的存在,会偷偷帮你做这两件事情,

INSERT INTO 子表 (ProposalId) VALUES (100)

1. 主动去查一下主表,看看id=100存在吗?  -- 隐式加锁
2. 锁定主表的那条记录,防止在你插入的过程中,主表数据被删掉 -- 加S锁

因为锁的存在,极大的提高了等待,死锁的可能性。

5. 为什么不支持分表分库

外键能生效,是因为数据库引擎能直接在内部执行检查,加锁,校验等功能,它只能管好自己
而互联网的架构往往是分布式,身份证表可能被分为N个表,N个库。它们甚至不在同一个库,同一个机器上。
那么问题来了,身份证表在数据库A,学生表在数据库B,如何建立外键关系? 别忘了,数据库只能管好自己。更何况跨服务器的情况,那更加鞭长莫及了。