依赖外键级联不好吗?
Posted
技术标签:
【中文标题】依赖外键级联不好吗?【英文标题】:Is it bad to rely on foreign key cascading? 【发布时间】:2011-01-13 08:54:07 【问题描述】:我参与的一个项目的首席开发人员说,依靠级联删除相关行是一种不好的做法。
我不明白这有多糟糕,但我想知道你对如果/为什么会这样的想法。
【问题讨论】:
When/Why to use Cascading in SQL Server? 的可能重复项。另外,我更倾向于同意另一个问题的答案,这与这里的答案完全不同。 【参考方案1】:首先我会说我很少删除行句号。通常,您要保留的大多数数据。您只需将其标记为已删除,这样它就不会显示给用户(即,对他们来说,它似乎已删除)。当然,这取决于数据,并且对于某些事情(例如购物车内容),当用户清空他或她的购物车时实际删除记录是可以的。
我只能假设这里的问题是您可能无意中删除了您实际上并不想删除的记录。然而,参照完整性应该防止这种情况发生。因此,除了明确的理由之外,我真的看不出反对这一点的理由。
【讨论】:
我必须同意 cletus 的观点,除非存在法律或实际容量问题……存储是如此便宜和快速,如今几乎没有真正的理由进行硬删除。 +1 这是一个原因 - 因为有人忘记了AND DELETED = 0
,删除的数据又爬回到了查询结果中。如果您这样做,请将所有已删除的数据隐藏在仅实时数据视图或类似视图后面,否则您将遇到问题。
@Nick:是的。但是,您从不使用 Query Analyzer 接触您的数据,甚至不去 DBA 工作?没有外部报告工具、Excel 电子表格等?
这就是视图的用途。如果需要,请将表命名为 foo_historical
和视图 foo
。
当上传了不良数据并且您需要替换它时,您必须删除它:) 我喜欢 Cascade。我正在寻找有关它如何向其他人证明我们为什么应该实施的信息。对我来说似乎很有效,但内存分配存在问题......【参考方案2】:
我会说你遵循最小意外原则。
级联删除不应导致数据意外丢失。如果删除需要删除相关记录,并且用户需要知道这些记录将消失,则不应使用级联删除。相反,应该要求用户明确删除相关记录,或提供通知。
另一方面,如果表与另一个表相关,该表本质上是临时的,或者包含一旦父实体消失就不再需要的记录,那么级联删除可能是可以的。
也就是说,我更喜欢明确说明我的意图,方法是删除代码中的相关记录,而不是依赖级联删除。事实上,我从未真正使用级联删除来隐式删除相关记录。此外,正如 cletus 所述,我经常使用软删除。
【讨论】:
这是一个很好的原则,有足够的自然发生的惊喜 - 无需制造更多。 完全同意这一点,除了“标记的解决方案”,软删除不是黄金。当你在桌面上除了 PK 之外还有其他独特的约束时,它很容易成为一个大问题。仅在需要时才进行软删除。 我喜欢你的考虑。我得出的结论是,在使用时,在代码中对其进行注释即可。它胜过协调删除【参考方案3】:我从不使用级联删除。为什么?因为太容易出错了。要求客户端应用程序显式删除(并满足删除条件,例如删除 FK 引用记录)要安全得多。
事实上,可以通过将记录标记为已删除或移至档案/历史表来避免删除本身。
在将记录标记为已删除的情况下,这取决于标记为已删除数据的相对比例,因为SELECT
s 将必须过滤“isDeleted = false
”,因此只有小于 10% 时才会使用索引(大约,取决于 RDBMS)记录被标记为已删除。
您更喜欢以下两种情况中的哪一种:
开发者来找你,说“嘿,这个删除不起作用”。你们俩都调查了一下,发现他不小心试图删除整个表格内容。你们俩都笑了,然后回到你正在做的事情上。
开发者来找你,害羞地问“我们有备份吗?”
不使用级联 UPDATES 或 DELETES 的另一个重要原因是:它们持有一个可序列化的锁。持有可序列化的锁会降低性能。
【讨论】:
假设我要从系统中支持一个订单:订单 -> 订单项 -> 评论。如果我删除订单(应用程序中的确认提示,自然),其他 2 个表中的行现在无用。如果您没有设置恢复父记录(在这种情况下,您没有删除......)通常孩子不再重要,更容易从一开始就在一个数据库中将它们一起级联删除。就像我说的,应用和意见的问题,但它们确实有它们的用途。 @NickCraver,如果您正确设置了外键(如果没有,您将无法设计数据库),那么您描述的场景就不会发生。如果你傻到依赖应用程序来建立关系,那么数据库完整性问题的可能性接近 100%。 @HLGEM 什么是“正确”的方式 - 这不是删除级联的设置吗?我经常有这些问题的另一种表类型是“多对多”关系表,它结合了从表 A 到 B 的条目。如果 A(或 B)被删除,该表会发生什么情况? @Nick Craver:*** 主数据库是否使用级联删除?【参考方案4】:避免级联删除的另一个重要原因是性能。在您需要从主表中删除 10,000 条记录之前,它们似乎是个好主意,而主表中又包含数百万条记录在子表中。鉴于此删除的大小,它可能会完全锁定所有表数小时甚至数天。你为什么要冒这个险?为了方便花费十分钟的时间为一条记录删除编写额外的删除语句?
此外,当您尝试删除具有子记录的记录时遇到的错误通常是一件好事。它告诉您不想删除此记录,因为您需要的数据如果这样做会丢失。级联删除将继续删除子记录,从而导致订单信息丢失,例如,如果您删除了过去有订单的客户。这种事情会彻底搞砸你的财务记录。
【讨论】:
我不明白你的第一个场景。替代方案将如何避免性能开销?你会干脆不删除子记录吗?还是因为删除以某种方式锁定了导致问题的表,而单独的删除避免了这种情况? 您将分批从子表中删除,然后进行主删除。我不允许任何人在生产企业类型数据库中使用级联删除。这太可能导致生产问题。 如果需要删除很多行(在引用的表中),如果有的话,级联删除的性能会更好;您的代码不会比数据库中的代码运行得更快。另一种情况对我来说很奇怪——你不能“错误地”删除引用行。如果您已将 FK 声明为级联,则意味着您的意图是不允许引用行在没有引用行的情况下存在,这是一个完全可以接受的用例。 @HLGEM 这只是意味着这个功能被滥用了;这并不意味着这是一种不好的做法。 对于不知道自己在做什么的学习 SQL 的人来说,这是一种不好的做法。【参考方案5】:我同样被告知级联删除是不好的做法......因此在遇到使用它们的客户之前从未使用过它们。我真的不知道为什么我不应该使用它们,但认为它们非常方便,因为不必编写代码来删除所有 FK 记录。
因此,我决定研究它们为何如此“糟糕”,并且从我目前发现的情况来看,它们似乎没有任何问题。事实上,到目前为止我看到的唯一好的论点是上面提到的HLGLEM 关于性能的内容。但由于我通常不会删除这么多记录,我认为在大多数情况下使用它们应该没问题。我想听听其他人可能反对使用它们的任何其他论点,以确保我已考虑所有选项。
【讨论】:
删除时级联的问题是,如果您不小心从引用的表中删除了一个值,那么所有使用该值的行都将被删除(不仅仅是这些行中的值,而是整行) .看起来真的很愚蠢,但我今天才学会了这一点。 @Holonaut 我明白你在说什么,那会是个问题。但是,如果您不小心以编程方式删除了一条记录,就像我通常单击按钮所做的那样,我会看到同样的问题。【参考方案6】:我要补充一点,ON DELETE CASCADE 使得使用二进制日志复制在数据仓库中维护数据副本变得很困难,这是大多数商业 ETL 工具的工作方式。从每个表中显式删除可维护完整的日志记录,并且对数据团队来说更容易:)
【讨论】:
【参考方案7】:我实际上同意这里的大多数答案,YET并非所有场景都是相同的,这取决于手头的情况以及该决定的熵是多少,例如:
如果您对与大量实体具有多个多/属于关系的实体有一个删除命令,则每次调用该删除过程时,您还需要记住从每个关系枢轴中删除所有相应的 FK与 A 有对应关系。
而通过删除时的级联,您将其编写为架构的一部分,它将仅删除那些相应的 FK 并从不再需要的关系中清除枢轴,想象一下 24 个关系一个实体 + 其他实体,在此之上也会有大量的关系,同样,这真的取决于你的设置和你觉得舒服的东西。无论如何,仅供参考,在 Illuminate 迁移模式文件中,您可以这样编写:
$table->dropForeign(['permission_id']);
$table->foreign('permission_id')
->references('id')
->on('permission')
->onDelete('cascade');
【讨论】:
以上是关于依赖外键级联不好吗?的主要内容,如果未能解决你的问题,请参考以下文章