-
使用 RESTRICT 会在更新或者删除记录时对外键对应的记录是否存在进行一致性检查;
-
使用 CASCADE 会在更新或者删除记录时触发级联更新或者删除操作;
注意:MySQL 中的 NO ACTION 和 RESTRICT 具有相同的语义5。
接下来我们会详细介绍关系型数据库如何处理上述两种不同类型的外键,而我们应该如何在应用中模拟这些功能。
一致性检查
当我们使用默认的外键类型 RESTRICT 时,在创建、修改或者删除记录时都会检查引用的合法性。想要在 MySQL 等数据库中触发外键的一致性检查其实非常容易,假设我们的数据库中包含 posts(id, author_id, content) 和 authors(id, name)两张表,在执行如下所示的操作时都会触发数据库对外键的检查:
-
向 posts 表中插入数据时,检查 author_id 是否在 authors 表中存在;
-
修改 posts 表中的数据时,检查 author_id 是否在 authors 表中存在;
-
删除 authors 表中的数据时,检查 posts 中是否存在引用当前记录的外键;
作为专门用于管理数据的系统,数据库与应用服务相比能够更好地保证完整性,而上述的这些操作都是引入外键带来的额外工作,不过这也是数据库保证数据完整性的必要代价。上述的这些分析都是理论上的定性分析,我们其实可以简单的定量分析一下引入外键对性能的影响。
在这里我们在数据库中同时创建 authors、posts 和 foreign_key_posts 三种表,如下所示,其中 posts 和 foreign_key_posts 两个表中的列完全相同,只是 foreign_key_posts 表为 author_id 字段增加了 RESTRICT 类型的外键约束:
图 3 - 外键性能测试关系图
我们先在 authors 表中插入一条记录,随后分别在 posts 和 foreign_key_posts中插入多条新数据列引用该条记录,前者不会检查外键的合法性,而后者会做额外的检查。你可以在 这里 找到作者用来测试外键额外开销的 Go 语言代码6,经过多次基准测试,我们可以得到如下所示的结果:
BenchmarkBaseline-8 3770 309503 ns/op
BenchmarkForeignKey-8 3331 317162 ns/op
BenchmarkBaseline-8 3192 315506 ns/op
BenchmarkForeignKey-8 3381 315577 ns/op
BenchmarkBaseline-8 3298 312761 ns/op
BenchmarkForeignKey-8 3829 345342 ns/op
BenchmarkBaseline-8 3753 291642 ns/op
BenchmarkForeignKey-8 3948 325239 ns/op
作者执行了 4 次外键的基准测试,虽然 4 次测试的结果不是特别稳定,但是使用外键的用例在每次测试中都明显弱于不使用外键的用例,外键带来的额外开销分别为 2.47%、0.02%、~10.41% 和 ~11.52%。这里的基准测试只是一个比较简单的定量分析,但是我们也可以从结果中看到大概的趋势 — 外键的完整性检查确实会带来额外的性能开销,而这些开销在高并发的服务中需要慎重考虑。
想要在应用程序中模拟数据库外键的功能其实比较容易,我们只需要遵循以下的几个准则:
-
向表中插入数据或者修改表中的数据时,都应该执行额外的 SELECT 语句确保它引用的数据在数据库中存在;
-
在删除数据之前需要执行额外的 SELECT 语句检查是否存在当前记录的引用;
需要注意的是为了保证一致性,我们需要在事务中执行上述的查询和修改语句,这样才能完整模拟外键的功能;当我们向 posts 表中插入或者修改数据时,需要的处理相对比较简单,我们只需要执行有限的 SELECT 语句并按照如下所示的模式执行对应的操作就可以了:
BEGIN
SELECT * FROM authors WHERE id = <post.author_id> FOR UPDATE;
– INSERT INTO posts … / UPDATE posts …
END
但是如果我们要删除 authors 表中的数据,就需要查询所有引用 authors 数据的表;如果有 10 个表都有指向 authors 表的外键,我们就需要在 10 个表中查询是否存在对应的记录,这个过程相对比较麻烦,不过也是为了实现完整性的必要代价,不过这种模拟外键方法其实远比使用外键更消耗资源,它不仅需要查询关联数据,还要通过网络发送更多的数据包。
级联操作
当我们在关系型数据库中创建外键约束时,如果使用如下所示的 SQL 语句指定更新或者删除记录时使用 CASCADE 行为,那么在客户端更新或者删除数据时就会触发级联操作:
ALTER TABLE posts
ADD CONSTRAINT FOREIGN KEY (author_id)
REFERENCES authors(id)
ON UPDATE CASCADE
ON DELETE CASCADE;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
总结
蚂蚁面试比较重视基础,所以Java那些基本功一定要扎实。蚂蚁的工作环境还是挺赞的,因为我面的是稳定性保障部门,还有许多单独的小组,什么三年1班,很有青春的感觉。面试官基本水平都比较高,基本都P7以上,除了基础还问了不少架构设计方面的问题,收获还是挺大的。
经历这次面试我还通过一些渠道发现了需要大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。
经历这次面试我还通过一些渠道发现了需要大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。
[外链图片转存中…(img-ZFYIMCaS-1711815487290)]