数据库为什么数据库不应该使用外键( 三 )

  • 当客户端更新 authors 表中记录的主键时 , 数据库会同时更新 posts 表中所有引用该记录的外键;
  • 当客户端删除 authors 表中的记录时 , 数据库会删除所有与 authors 表关联的记录;
不过无论是执行更新还是删除操作 , 数据库都可以保证各个关系表之间引用的一致性和合法性不会出现引用到不存在记录的情况 , 与 RESTRICT 行为一样 , 所有外键的更新和删除行为都可以通过执行额外的检查和操作保证数据的一致 。
数据库为什么数据库不应该使用外键
本文插图
图 4 - 复杂的级联操作
虽然级联删除的出发点也是保证数据的完整性 , 但是在设计关系表之间的不同关系时 , 我们也需要注意级联删除引起的数据大规模删除的问题 。 如上图所示 , 当客户端想要在数据库中删除 authos 表中的数据时 , 如果我们同时在 authors 和 posts中指定了级联删除的行为 , 那么数据库会同时删除所有关联的 posts 记录以及与 posts 表关联的 comments 数据 。
这种涉及多级的级联删除行为在数据量较小的数据库中不会导致问题 , 但是在数据量较大的数据库中删除关键数据可能会引起雪崩 , 一条记录的删除可能会被放大到几十倍甚至上百倍 , 这些对磁盘的随机读写会带来巨大的开销 , 是我们想要尽可能避免的情况 。 如果我们能够较好地设计各个表之间的关系并且慎用 CASCADE 行为 , 这对于保证数据库中数据的合法性有着很重要的意义 , 使用该特性可以避免数据库中出现过期的、不合法的数据 , 但是在使用时也要合理预估可能造成的最坏情况 。
手动实现数据库的级联删除操作是可行的 , 如果我们在一个事务中按照顺序删除所有的数据 , 确实可以保证数据的一致性 , 但是这与外键的级联删除功能没有太大的区别 , 反而会有更差的表现 。 如果我们能够接受在一个时间窗口内的数据不一致 , 就可以将一个大号的删除任务拆成多个子任务分批执行 , 降低对数据库影响的峰值 。
DELETE FROM posts WHERE author_id = 1 LIMIT 100;DELETE FROM posts WHERE author_id = 1 LIMIT 100;...DELETE FROM authors WHERE id = 1; 与数据库外键的 CASCADE 相比 , 这种方式会带来更大的额外开销 , 只是我们能降低对数据库性能的瞬时影响 。
总结 外键提供的几种在更新和删除时的不同行为都可以帮助我们保证数据库中数据的一致性和引用合法性 , 但是外键的使用也需要数据库承担额外的开销 , 在大多数服务都可以水平扩容的今天 , 高并发场景中使用外键确实会影响服务的吞吐量上限 。 在数据库之外手动实现外键的功能是可能的 , 但是却会带来很多维护上的成本或者需要我们在数据一致性上做出一些妥协 。 我们可以从可用性、一致性几个方面分析使用外键、模拟外键以及不使用外键的差异:
  • 不使用外键牺牲了数据库中数据的一致性 , 但是却能够减少数据库的负载;
  • 模拟外键将一部分工作移到了数据库之外 , 我们可能需要放弃一部分一致性以获得更高的可用性 , 但是为了这部分可用性 , 我们会付出更多的研发与维护成本 , 也增加了与数据库之间的网络通信次数;
  • 使用外键保证了数据库中数据的一致性 , 也将全部的计算任务全部交给了数据库;
在大多数不需要高并发或者对一致性有较强要求的系统中 , 我们可以直接使用数据库提供的外键帮助我们对数据进行校验 , 但是在对一致性要求不高的、复杂的场景或者大规模的团队中 , 不使用外键也确实可以为数据库减负 , 而大团队也有更多的时间和精力去设计其他的方案 , 例如:分布式的关系型数据库 。
当我们考虑应不应该在数据库中使用外键时 , 需要关注的核心我们的数据库承担这部分计算任务后会不会影响系统的可用性 , 在使用时也不应该一刀切的决定用或者不用外键 , 应该根据具体的场景做决策 , 我们在这里介绍了两个使用外键时可能遇到的问题:


推荐阅读