关系数据库中的完整性约束-我们应该忽略它们吗?


10

我正在与我工作的公司的开发人员进行永久性讨论,因为他们说最好摆脱关系数据库中的关系强制(通过FOREIGN KEY约束定义),以便加快大型查询并获得更好的结果。性能。

所考虑的平台是MySQL 5.x,并且尚未设置FOREIGN KEY,甚至缺少相关表的一些PRIMARY KEY约束,至少对于我来说,这是不合理的。也许他们是对的,但我是错的,但我没有足够的论点来讨论这种情况。

三年来,这一直是首选方法。我是这家公司的新手(只有一个月),但是随着产品的“上市”,人们在犹豫是否要增强数据库。话说回来,我注意到的第一件事是一页需要1分钟的加载时间(是的,需要60秒!)。

当前事务状态背后的一种说法是,“非规范化”数据库比规范化数据库要快,但我认为那不是真的。

大多数相关查询都包含JOIN操作,这使它们在处理大量数据(数据库包含数百万行)时非常非常非常慢地运行。

通常,“ CRUD”操作的处理是在应用程序代码级别实现的;例如,为了删除一些数据自,例如TableA

  • 必须首先即时检查TableA和的行之间是否存在某种关系TableB
  • 如果上述关系被“检测到”,则应用程序代码将不允许删除相关行,但是
  • 如果由于某种原因该应用程序代码失败,则无论涉及的行和表是否存在任何关系,DELETE操作都将“成功”进行。

您能帮我拟定一个良好,准确而可靠的答案以丰富辩论的内容吗?


注意:也许以前有人问过(并回答过)类似的问题,但是我无法通过Google找到任何东西。


评论不作进一步讨论;此对话已转移至聊天
保罗怀特9

Answers:


12

如您的帖子所述,如果打算创建一个关系数据库(为简便起见,请使用RDB),因此,希望它能正常运行,那么简短的答案是:

  • 不,您不应忽略数据完整性约束

主要目标应该是按原样管理相关数据,这是非常有价值的组织资产,并且要实现该目标的可靠方法是采用可靠理论支持的技术手段。

因此,作为数据库专业人员,您可以利用EF Codd博士提供的最新和优雅的关系模型机制来实施业务规则,并避免如果不加以利用最终会出现的问题。

在这方面,我将分享(a)我的总体约束,以及(b)关于数据库事务状态和所讨论的工作环境的以下几点考虑。

外键约束,数据关系和参照完整性

RDB必须高精度地反映感兴趣的业务环境的特征,这绝对需要由遵循最佳实践的建模者或设计者领导的深入概念级分析,并依靠业务专家的不可或缺的协助。该分析必须正确识别并制定适用的业务规则

因此,如果这样的建模者已经确定相关数据之间存在相互关系,则他或她必须配置相应的逻辑级别限制,以便数据库管理系统(DBMS)可以保证数据与确切特征保持一致,并且始终在上述分析中确定的规则。

关于正在讨论的数据库,可以推断出已经确定了相关的相互关系,因为您提到存在一种程序化的(并且容易规避的)尝试,通过使用应用程序代码(在数据库管理系统之外)来强制执行它们。是一种关系前的方法),在任何情况下都必须“触摸”数据库以尝试验证所述相互关系的完整性。

但是,正如您所知,这并不是保护参照完整性的最佳技术,因为关系科学为此目的规定了一种非常强大的工具,即外键(FK)约束。这些约束很容易创建(通过高级声明方法),因为它们是单个句子,避免了诉诸不必要且容易出错的临时程序。值得注意的是,FK约束的执行速度已由专门的程序员进行了高度优化(并且甚至几十年来,主要的平台供应商都在致力于此)。

此外,由于RDB必须是能够被多个应用程序(桌面,自动,Web,移动,其组合)访问的独立(自我保护,自我描述等)软件组件,因此不应与任何这些应用程序的代码“耦合”。

同样,作为重要的组织资源的数据自然倾向于不存在应用程序,应用程序程序员,应用程序开发平台和编程范例。

主键约束和重复行的含义

从概念上讲,当某种特定的事物在业务环境中被认为具有重要意义时,数据库建模人员必须(1)确定其相关特征(即其属性),并确认该事物作为实体实例原型,也就是说,实体类型-和(2)通过在逻辑设计中由一个或多个集成的来表示它。

然后,就像区分现实世界中给定实体类型的每个单独实例一样至关重要表中包含的每一行也必须唯一地区分。如果一个表没有声明任何KEY,它将最终保留重复项,如果有两行或更多行保留完全相同的值,则它们都具有相同的含义,它们都表示相同的事实

此时,由于多种原因,应丢弃重复的行。从理论上讲,设计者必须确保每一行总是唯一的,以使表在SQL数据子语言允许的范围内具有相关性(对数据操作有重要影响)。此外,从信息的角度来看,如果多行表示相同的事实,则记录它们不仅是多余的,而且是有害的,例如以下所示:

  • 假设某人在某个表中插入了两个相同的行。
  • 后来,其他人来了,只更新了一次重复项。结果,其他事件不再是最新的。
  • 随后,另一个人更新了到目前为止尚未修改的事件。以这种方式,两个副本在不同的时间点经历了不同的变化。
  • 之后,当某人对选择有关行所传达的信息感兴趣时,他或她可以找到该行的两个不同“版本”。

通过这种方式:

  • 哪个“版本”可以被认为是正确,可靠的版本?
  • 哪一个准确地反映了现实世界?

如您所知,这种现象甚至可能具有法律上的含义,这种情况无疑具有极其重要的意义。

此外,处理此类矛盾所花费的时间和精力(也许通过某种“更新同步”)应该更好地用于真正为组织创造价值的任务。因此,应通过设计避免保留矛盾的行以保持数据库的完整性。

这就是为什么必须始终由数据库设计人员执行PRIMARY KEY(PK)的标识相应约束的声明的原因。但也必须提及的是,一个表可能具有不止一个列或列的组合,其中包含唯一标识每一行的值。因此,除了设置PK约束(出于实用性原因理想地设置为PRIMARY)之外,设计人员还必须在适用时声明一个或多个ALTERNATE KEY(通常通过一个或多个UNIQUE加NOT NULL约束进行定义)很常见)。

PK的另一个有利特性是,当“迁移”到其他表以参与单个或复合FK时,它们可以帮助增强数据之间存在的关系的基数比。是的,所有这些都是通过DBMS确保的简单有效的声明设置来实现的。

(当前)CHECK约束和单行验证

让我们不要忘记(当前)CHECK约束的相关性,这些约束通过声明性地限制行的有效列值集(可能看起来很简单,但实际上是关系DBMS的基本特征),也有助于实现确保业务环境的规则始终准确地反映出来。

当您用MySQL标记标记问题时,必须指出的是,不幸的是,这样的平台允许声明这种约束,但是同时忽略了它的执行!可以理解的,这种情况自2004年以来一直被报告为错误

在这一点上,你将不得不通过其他方式,例如,为了照顾这个因素的ACID事务,触发器或数据库管理系统本身的其他方法(见本答案@ypercubeᵀᴹ 有关此主题的信息),从而使数据继续始终如一。

声明约束:以声明方式建立更多的多行和多表业务规则

不论出于何种原因,包括MySQL在内的不同SQL DBMS都(即使有)支持程度很差的一个方面是以声明方式(显然是PK和FK之外)启用了多行和多表约束。

就其本身而言,SQL标准确实包含了很多年以来的ASSERTION。我不知道您的业务环境中的哪些规则将从该逻辑级别的验证方法中受益,但是作为数据库设计人员,我认为使用一个或多个ASSERTION约束数据非常方便,尽管我必须从从DBMS开发人员的角度来看,这种最重要的工具一直很难在物理抽象级别上实现。

自2016年以来,Oracle供应商和/或开发人员似乎正在评估 ASSERTION支持,这将使DBMS更加符合关系,从而更加健壮和更具竞争力。我猜想,如果(i)他们的使用者继续努力,并且(ii)Oracle成功实施,那么(iii)其他DBMS供应商/社区也必须启用它们,并且它们的使用范围将开始扩大。当然,这将是数据库管理领域的巨大进步,并且是Codd博士设想的最独特的工具之一,我个人希望我们很快会看到这种情况。

数据一致性和决策过程

如上所述,RDB的最重要方面之一是它自己保证所保留数据的一致性,并且只有当RDB遵守建模者声明的完整性约束时,才能满足所述一致性。

在这方面,必须具有受完整性保护的表(在DDL结构中建立的表),以便能够创建值得信赖的派生表(例如SELECT语句或从多个表中检索列的视图)。,因为必须根据基本表来生成派生表。

众所周知,人们将信息用作组织(和普通)决策过程中的主要工具。然后,如果数据库提供的信息不连贯且不准确,则基于此类信息的决策将不合理(至少可以说)。这就是为什么必须仔细设计和实施RDB的原因:应该将RDB构建为可靠的资源,可以帮助其用户做出有根据的决策。

“非正规化”

遗憾的是,“非规范化的数据库比规范化的数据库要快”,这是一个广泛传播的误解,尽管它也是可以从逻辑,物理和实用主义的角度加以反驳的论点。

首先,非规范化意味着必须预先对基表进行规范化(借助于在数据库的逻辑抽象级别上实现的基于科学的形式化程序)。

因此,假设该表实际上已正确归一化,则将其“去归一化”(与该词的形式含义相反,这涉及在表中附加属于广告中其他表的列,这些列也属于广告的其他表)临时方式)可能有助于(例如,在物理级别上)仅加快一个或几个特定SELECT语句的处理速度,而这种做法可能同时破坏许多其他关联数据的执行操作操作(例如,多个INSERT,UPDATE,DELETE和SELECT语句,或包含在单个或多个ACID事务中的它们的组合)。

此外,非规范化(正式或非正式)都会引入更新/修改异常,从而破坏数据库的一致性,而这可以通过复杂,昂贵且容易出错的过程来“解决”,而这一切都可以避免。一开始。

支持规范化和“非规范化”表的物理层架

打算在现实世界中使用的逻辑(抽象)布局(SQL-DDL设计)显然拥有必须考虑的物理(具体)影响。

以这种方式,“非规范化”表必定会“更宽”(容纳更多列),这意味着其行必定会更重(需要更多和更大的物理层组件),因此意味着底层计算过程(例如,那些与硬盘驱动器或内存有关的内容很容易变慢。

相反,归一化表格当然“更窄”(具有更少的列)将是“更轻”的元素(由越来越少的物理组件提供服务),其“表现得更快”,这将加快与例如数据写入和读取。

这样,非常方便(a)正式,谨慎地规范相关表,保持它们不变,然后(b)利用可以优化数据检索和修改速度的任何物理级别资源,例如,实现谨慎有效的索引策略,启用正确的软件和硬件服务器配置,升级网络带宽功能等。

正在考虑的数据库的功能

问题的以下几段与数据检索操作的速度有关:

[A]如果产品“有效”,则在增强数据库方面犹豫不决;不过,我注意到的第一件事是一页加载需要1分钟(是的,需要60秒!)。

如果某个页面的负载如此之大,则很明显,系统的用户无法获得良好的服务;因此,即使“工作”起来,它的功能似乎也不是最优的,这表明您有意维持整个环境(数据库和应用程序)效率更高的意图,并且表现出非常建设性的态度。

然后,即使科学绝对支持您,因此您也应保持坚定的态度,但我还是建议您以外交方式处理此情况,因为最终,您的雇主,同事和您自己正在共同努力,以使整个组织更成功。因此,这是您应该强调的一个论据,即尽管他们在做其他事情时做得很好,但是改善常规和特定数据管理实践可以极大地帮助实现组织和个人的增长。

大多数相关查询都包含JOIN操作,这使它们在处理大量数据(数据库包含数百万行)时非常非常非常慢地运行。

值得注意的是,JOIN运算符是与数据的关系处理有关的必不可少功能强大的元素。然后,尽管功能更强大的平台以相对更快的执行速度为其提供了服务,但是您描述的情况很可能是效率低下的设计(在抽象的概念,逻辑和物理级别)的症状。因此,我的第一眼估计是:

  • INDEX设置可能需要改进。
  • PK和FK列的类型和大小定义需要进行审查(并且我完全同意@Rick James的PK 注意事项,因为在适当的情况下,复合KEY往往比附加的代理更有效)。
  • 由于在适当的情况下(即在设计良好的RDB中执行),JOIN执行得非常快,因此进一步的(基于科学的正式)规范化可能有助于减轻这些问题。

此外,是的,正如@TommCatt他的回答中提到的那样,有时对查询的(逻辑)重写会修改其(物理)执行计划,从而加速数据的读取/写入,这是一个应明确考虑的因素。


1
好答案。在考虑实现的性能时,我总是提醒自己,一个比我聪明得多的开发人员团队已经从事很长时间的工作了。关系数据库是世界上最庞大的系统(Facebook和Twitter仅举几例)的核心。
尼克·贝德福德'18

9

开发人员的基本前提是绝对错误的。外键会稍微影响系统DML的性能。它们根本不在查询中使用,因此对其性能没有影响。因此,您的开发人员不知道他们在说什么,而您是应该考虑向他们咨询的最后人员。

外键在维护数据完整性方面起着至关重要的作用。这比删除它们所带来的任何微小性能改进(甚至是事实)要重要得多。

任何情况下,请勿从OLTP数据库中删除FK。

同样,非规范化有时会加快某些查询的速度。正如他们所说,这取决于。尽管如此,即使速度有所提高,通常也不值得为保持数据完整性付出额外的努力。

当简单的调优不能比非规范化带来更多的速度改进时,这种情况很少见。一个好的DBA可以(最终)在这里赚钱。您还可以调整查询。我曾经进行过一次查询,该查询在不少于30分钟的时间内返回了答案,并使其在8秒钟内起作用。无需更改数据库,只需重写查询即可。当然,这是我个人最好的记录,因此您的里程可能会有所不同,但是非规范化应该是您尝试的最后一件事。

您可能还希望开发人员不要编写更复杂的查询。询问他们所需的数据以及所需的格式,然后提供视图以将其提供给他们。复杂的查询将成为视图。然后,开发人员只需编写:

select <something> from <SomeView> where <whatever>;

我还假设您的数据库经过精心设计。一个糟糕的数据库设计,甚至是数据库的一小部分,都会使速度变慢。我经常使用非常大的表(每个数以十亿计的记录)进行查询,这些查询将它们左右合并在一起,并在几分之一秒之内就能得到(得到)答案。表的大小并不决定查询的速度。

当有人说:“因为产品'行得通',所以我会毫不犹豫地增强数据库。” 如果这种“犹豫”更像是“不在我的手表上,朋友!” 那么您甚至可能想要开始更新您的简历。这样的环境永远不会带来任何好处,即使您可能已经游说了数小时来做出可以防止失败的更改,但您将为以后的每次失败承担责任。您会反复听到“现在不是进行更改的好时机”。对。祝好运。


需要注意的一件事是,有时您需要根据要返回的数据量对同一数据进行不同的查询。例如,返回单行(甚至只是一个计数)的查询可能比返回数千条记录的查询写得更好。
乔W

2

更改标题会更改问题。 FOREIGN KEYs是可选的。他们是这样:

  • FK INDEX在其中一个表中隐式创建一个。可以手动添加这样的索引。(因此,不需要 FK 。)
  • FK检查完整性。这是韩国的名望。不需要 FK,因为您的应用程序可以执行类似的检查,或确定不需要检查。所以...
  • 完整性检查会在性能上付出一些代价;因此会减慢处理速度。(通常这没什么大不了的。)
  • FK并没有做到每个人都想要的一切。这个论坛上充斥着“为什么FK不能X”的问题。特别是该CHECK选项未生效。
  • FK可以做到CASCADE。(就我个人而言,我更喜欢保持控制,而不是假设德国联邦将“做正确的事”。)

FK的底线:有些人坚持使用FK。如果没有它们,有些产品会生活得很好。你决定。

摆脱PRIMARY KEYInnoDB是一个大错误。另一方面,摆脱代理AUTO_INCREMENT并使用由一个(或多个)列组成的“自然” PK通常是正确的选择。一个简单的,常见的情况是一个许多:许多映射表,如所讨论这里

根据个人经验,我建议2/3的桌子帽最好使用“自然”而不是auto_inc PK。


1
所以……您依赖于一个几乎完美的应用程序,因为如果开发人员DELETE例如犯了一个错误并且您对数据库方面没有任何限制,那么您将最终丢失数据。这种方法是有效的,但是需要大量的代码和良好的测试,而他们没有:)
ReynierPM

删除太多内容可能会在应用程序或FK中发生。删除得太少通常很明显。OTOH,我看到过这样的情况:删除太少是值得的-想想“标准化”,很少删除任何东西。多余的未使用的行实际上是无害的。
瑞克·詹姆斯

我已经看到一个 “好”情况,即表上没有索引-一种用于高速提取的临时表。它是非常短暂的(因此不需要InnoDB),只需要完全读取(因此,不需要索引)。
瑞克·詹姆斯

1
请注意我在杂谈中的一个共同主题:没有一个答案。没有一个适合所有人。
瑞克·詹姆斯

如果表长一千行;性能不是问题。如果您的表长十亿行,则需要检查有关规范化,PK,索引,FK,UUID等的所有“规则”。否则数据库将崩溃。
瑞克·詹姆斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.