Java进阶面霸|为什么数据库不应该使用外键

作者:Draveness
来源:真没什么逻辑
为什么这么设计(Why’sTHEDesign)是一系列关于计算机领域中程序设计决策的文章 , 我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响 。 如果你有想要了解的问题 , 可以在文章下面留言 。
当我们想要持久化地存储数据时 , 使用关系型数据库往往都是最稳妥的选择 , 这不仅因为今天的关系型数据库种类非常丰富并且稳定 , 还因为不同社区对关系型数据库的支持都非常完备 。 我们在前面的文章中曾经分析过为什么MySQL的自增主键不单调也不连续 , 这篇文章我们来分析关系型数据库中另一个重要的概念—外键(ForeignKey) 。
在关系型数据库中 , 外键也被称为关系键 , 它是关系型数据库中提供关系表之间连接的多个列1 , 这一组数据列是当前关系表中的外键 , 也必须是另一个关系表中的候选键(CandidateKey) , 我们可以通过候选键在当前表中找到唯一的元素2 。 在通常情况下 , 我们都会使用关系表中的主键作为其他表中的外键 , 这样才可以满足关系型数据库对外键的约束 。
Java进阶面霸|为什么数据库不应该使用外键
文章图片
图1-关系型数据库与外键
外键不仅仅是数据库表中的一个整数 , 它还提供了额外的一致性保证 。 因为数据库往往是整个系统的真理之源(SourceofTruth) , 所以保证数据的一致性和正确性非常重要 , 关系型数据库虽然提供了外键、触发器等特性保证一致性 , 但是在今天的生产环境中却很少被使用 。
【Java进阶面霸|为什么数据库不应该使用外键】引用完整性(ReferentialIntegrity)是数据的属性 , 如果数据拥有该属性 , 那么数据中所有的引用都是合法的 , 在关系型数据库的上下文中 , 这就意味着关系型数据库中引用另一个表中的值必须存在3 。
ALTERTABLEpostsADDCONSTRAINTFOREIGNKEY(author_id)REFERENCESauthors(id);上述SQL语句可以向关系表中增加外键约束 , 该SQL语句的执行前提是posts表中存在author_id字段 。 从SQL语句中的CONSTRAINT关键字我们也能推测出外键不是一种数据类型 , 它是不同关系表之间的约束 。
Java进阶面霸|为什么数据库不应该使用外键
文章图片
图2-无状态服务与数据库
不使用外键的原因其实很简单 , MySQL、PostgreSQL等关系型数据库很难水平扩容 , 但是无状态的服务往往都可以很容易地扩容 。 由于外键等特性需要数据库执行额外的工作 , 而这些操作会占用数据库的计算资源 , 所以我们可以将大部分的需求都迁移到无状态的服务中完成以降低数据库的工作负载 。
根据更新和删除时的行为不同 , 我们可以将外键分成RESTRICT、CASCADE和SETNULL等几种4 , 当我们为关系表中的字段增加外键约束时 , 需要指定外键的类型 , 最常见的也就是RESTRICT和CASCADE两种 , 其中RESTRICT为外键的默认类型 , 不同类型的外键会带来不同的额外开销 , 而这些额外开销就是我们不使用外键的理由:
使用RESTRICT会在更新或者删除记录时对外键对应的记录是否存在进行一致性检查;使用CASCADE会在更新或者删除记录时触发级联更新或者删除操作;注意:MySQL中的NOACTION和RESTRICT具有相同的语义5 。
接下来我们会详细介绍关系型数据库如何处理上述两种不同类型的外键 , 而我们应该如何在应用中模拟这些功能 。
一致性检查当我们使用默认的外键类型RESTRICT时 , 在创建、修改或者删除记录时都会检查引用的合法性 。 想要在MySQL等数据库中触发外键的一致性检查其实非常容易 , 假设我们的数据库中包含posts(id,author_id,content)和authors(id,name)两张表 , 在执行如下所示的操作时都会触发数据库对外键的检查:


推荐阅读