设置完整性约束会降低性能吗?

Does putting integrity constraints decrease performance?

在与朋友的讨论中,我听到了两件事 -

  1. 使用约束会导致性能略有下降。例如。考虑唯一性约束。在插入之前,DBMS 必须检查所有现有数据的唯一性,从而导致额外的计算。

  2. 他建议确保这些约束在应用程序级别逻辑本身得到处理。例如。自己正确地从 table 中删除行,而不是设置外部完整性约束等

第一个对我来说听起来有点合乎逻辑,但第二个在直觉上似乎是错误的。不过,我在 DBMS 方面没有足够的经验来真正判断这些说法。

问。索赔1是否正确?如果是这样,声明 2 是否是处理此类情况的正确方法?

说法一正确,说法二不正确,跟你的结论一样。

数据库的工作是处理数据及其完整性。应用程序的工作是向数据库询问有关数据的信息,然后 使用 该数据执行工作。

如果您通过申请处理#2:

  • 您必须处理并发性 - 当数据库有超过 1 个活动连接时会发生什么情况?您需要锁定表以执行确保唯一性或完整性的操作。由于此连接随时可能中断,因此您面临着一个巨大的问题。如何在锁定表的进程死亡时解锁表?

  • 没有比数据库本身更好的应用程序了。您仍然需要检查行的唯一性,这意味着您需要检索所有数据,对整个数据集执行检查,然后写入它。你不能比数据库做得更好或更快 - 根据定义,它会更慢,因为你需要将数据从数据库传输到你的应用程序

  • 创建数据库时考虑到了并发性。使用你朋友的逻辑创建优化会导致应用程序不稳定、数据重复、数据库无响应等。永远不要这样做。让数据库完成它的工作,它就是为此目的而设计的。

检查唯一性时,MySQL 使用索引,这是一种用于快速访问的数据结构。 MySQL 执行唯一性检查的速度在性能上是任何应用程序都无法比拟的 - 它只会更快地完成工作。如果您需要唯一数据,则需要确保您拥有唯一数据——这是一种无法避免的工作量,开发数据库的人员正在使用经过验证的专为提高速度而设计的算法。它已经以最佳速度运行。

至于完整性——同样,MySQL(或任何其他 RDBMS)就是用来处理这种情况的。如果在应用程序逻辑中实现外键约束会更好,那么我们一开始就不会使用 FK。就像我之前提到的 - 数据库的工作就是处理这个问题。

关系数据库的 ACID 不是无缘无故的。原子性、一致性、隔离性、持久性 MySQL 的 InnoDB 实现并允许这些,如果你需要它——那么你就可以使用它。与 MySQL 的内部处理相比,任何人都无法创建任何语言的应用程序,在任何方面都表现得更好。

TL;DR:您的想法是正确的。

约束通常会导致轻微性能下降。没有什么是免费的。但是,有两个重要的考虑因素:

  1. 性能损失通常很小,以至于在 运行 系统的自然可变性 "noise" 中丢失,因此需要进行涉及数千或数百万测试查询的测试才能确定差异。
  2. 有人要问 "Affects the performance where?" 约束会影响 DML 操作的性能。但是,如果不存在约束,则每个查询都必须执行额外的测试以验证所读取数据的准确性。我可以向您保证,这对性能的影响将远大于约束条件。

当然也有例外,但大多数数据库的查询频率远高于修改频率。因此,如果您可以将性能命中从查询转移到 DML,通常可以加快系统的整体性能。

务必在应用程序级别执行单独的约束检查。在收集数据的过程中向用户提供反馈 ("Delivery date cannot be in the past!") 而不是等到将数据插入数据库的尝试失败时,这是一个巨大的好处。

但这并不意味着将它们从数据库中删除。这种冗余很重要。你能绝对保证对数据库执行的唯一操作将源自应用程序吗?绝对不。应用程序外部进行了太多正常维护 activity,无法兑现承诺。更不用说通常有多个应用程序,因此保证必须适用于每个应用程序。未完待续的事情太多了。

在设计数据库时,数据完整性是您的第一要务。永远不要为了性能而牺牲它,特别是因为设计良好的数据库的性能通常不是问题,即使是,有太多方法可以提高性能而不涉及删除约束(或非规范化,另一个错误许多人仍然为了提高 OLTP 系统的性能而制作。

TL;DR

如果您的数据需要正确,则需要强制执行约束,如果您需要强制执行约束,让数据库为您做会比其他任何事情都更快(而且可能也更正确)。

例子

尝试在应用程序级别强制实施诸如密钥唯一性之类的事情可以正确或快速地完成,但不能两者兼而有之。例如,假设您要插入一个新行。一个天真的应用程序级算法可能看起来像这样:

  1. 在 table 中搜索新行的(关键字段)。
  2. 如果没有找到,插入新行。

这实际上适用于单客户端/单线程环境。但是,在 concurrent 环境中,其他一些客户端可能会在您的步骤 1 和步骤 2 之间写入相同的键值,然后转瞬即逝:您的数据中有一个副本,您甚至都不知道!

为了防止这种 竞争条件 ,您必须使用某种形式的锁定,并且由于您要插入 new行,还没有要锁定的行 - 您可能最终会锁定整个 table,破坏过程中的可伸缩性。

OTOH,如果你让 DBMS 为你做这件事,它可以用一种特殊的方式来做,而不需要太多的锁定,它已经在所有棘手的并发边缘情况下进行了正确性测试和双重测试,并且它的随着 DBMS 上市时间的推移,性能得到了优化。

foreign keys 也存在类似的问题。


所以是的,如果您的应用程序是唯一一个访问数据库的应用程序(例如,当使用嵌入式数据库时),您可能会逃避应用程序级别的强制执行,但如果 DBMS 可以为您执行此操作,您为什么要这样做呢?

但在并发环境中,将键和外键留给数据库 - 无论如何,您将有大量工作,强制执行您的自定义 "business logic"(这不是直接 "declarable" 在 DBMS 中) 以一种既正确又高效的方式...

话虽这么说,请随意执行任何有利于您的用户体验的应用程序级 "pre-checks"。但是 除了 数据库级别的约束外,还要做它们,而不是代替它们。

是的,检查约束确实会花费时间并减慢数据库更新速度。

但完全不清楚将此逻辑移至应用程序将如何带来净性能改进。现在您至少有两次单独的数据库访问:一次检查约束,另一次执行更新。每次访问数据库的成本:建立连接需要时间,数据库引擎解析查询和构建查询计划需要时间,将结果发回需要时间。由于数据库引擎不知道你在做什么或为什么,它无法优化。在实践中,一个 "big visit" 几乎总是比完成同样事情的两个 "small visits" 便宜。

我在这里主要讲的是唯一性约束和关系完整性约束。如果您有一个可以在不访问数据库的情况下进行测试的约束,例如单个字段的范围限制,那么在应用程序中这样做会更快。由于各种原因,也许仍然不是一个好主意,但它会更快。

Q. Is the claim 1 correct ?

是的。根据我的经验,使用约束 可以 导致性能大幅下降。 性能影响与 table 中的约束和记录数量有关。随着 table 条记录的增长,性能会受到影响,数据库性能可能会快速从好变为坏。

例如。在我工作的一家审计公司中,部分过程是将包含大量 responsibilities/roles/functions 的 excel 矩阵序列化为一组具有许多 FK 约束的 table。 最初性能还不错,但在 6 个月到一年内,这个序列化过程需要几分钟才能完成。我们尽可能多地进行优化,但影响很小。如果我们关闭约束,这个过程会在几秒钟内完成。

If so (if claim 1 is correct), is claim 2 even the right way to handle such scenarios ?

是的,但在某些情况下。

  1. 您有大量的约束条件
  2. 您的数据库 table 中有大量/不断增长的记录。
  3. 提供的数据库硬件无论出于何种原因都无法改进,您遇到了性能问题。

因此,对于我们在审计公司遇到的性能问题,我们考虑将约束检查转移到应用程序数据集中。所以本质上,数据集用于检查和验证约束,而矩阵 DB tables 仅用于存储(和处理)。

注意:这对我们有用,因为矩阵数据一旦插入就不会改变,并且每个矩阵都独立于所有其他过去插入的矩阵。