什么时候以及为什么数据库连接昂贵?


354

我正在对数据库进行一些研究,并且正在研究关系数据库的一些局限性。

我知道大表的联接非常昂贵,但是我不确定为什么。DBMS需要执行什么操作才能执行联接操作,瓶颈在哪里?
非规范化如何帮助克服这一费用?其他优化技术(例如,索引编制)如何提供帮助?

欢迎亲身经历!如果您要发布资源链接,请避免使用Wikipedia。我知道在哪里可以找到。

与此相关,我想知道BigTable和SimpleDB等云服务数据库使用的非规范化方法。看到这个问题


3
您还在研究好处吗?;)
David Aldridge

我正在调查目标(如果有这样的事情)的比较。专业人士,骗局,您所拥有的。
瑞克

预先呈现的云计算方法基于能够以各种方式下注,从而避免了“错误连接”问题。Google在自己的系统上有一些白皮书。非常有趣-扩展特殊情况适用性的方法。
Peter Wone

@PeterWone-是否希望提供其中一些论文的参考?ps回答您个人资料中的问题,Android是开源的-至少部分是Android的,因此极客们一跃而入。伟大的未洗者认为它们在技术上是先进的,它们像旅鼠一样紧紧追随Google的紧身汗水!Betamax有人吗?更贴近我(和一代人)的心,当MySQL(没有FOREGIN KEYFFS)与PostgreSQL(没有本地Windows版本)和Firebird(Opensourcing惨败)竞争时,它如何(并保持)成为世界上最受欢迎的“ R” DBMS ,甚至SQLite?
Vérace

不用说,对于多用户系统,我认为PostgreSQL和Firebird 远远优于MySQL,而在单用户领域,SQLite则很出色。SQLite处理sqlite.org网站(每天点击量为40万!)。
Vérace

Answers:


470

去规范化以提高性能?听起来很令人信服,但它没有水。

与Ted Codd博士一起工作的Chris Date是关系数据模型的最初拥护者,他忍受了对归一化的误导性论证,并用科学的方法对其进行了系统地拆除:他拥有大型数据库并测试了这些断言。

我认为他是在1988-1991年间的关系数据库著作中写的,但是后来被本书卷入《数据库系统简介》第六版,这是有关数据库理论和设计权威文章,在我撰写本书时是第八版,并且很可能会保留下来。几十年来一直在印刷。当我们大多数人仍赤脚奔跑时,克里斯·达特曾是该领域的专家。

他发现:

  • 其中一些适合特殊情况
  • 他们都无法偿还一般用途
  • 对于其他特殊情况,所有这些都明显更糟

一切都归结为减轻工作集的规模。包含正确选择的索引和正确设置的索引的连接便宜而又不昂贵,因为它们可以实现行之前对结果进行大量修剪。

实现结果涉及批量磁盘读取,这是该练习中最昂贵的一个数量级。相比之下,执行联接在逻辑上仅需要检索。实际上,甚至不获取键值:键哈希值用于连接比较,从而减轻了多列连接的成本,并从根本上降低了涉及字符串比较的连接的成本。不仅更适合缓存,而且磁盘读取的工作量也要少得多。

而且,一个好的优化器将选择限制性最强的条件并在执行联接之前应用它,非常有效地利用联接对具有高基数的索引的高选择性。

诚然,这种类型的优化也可以应用于非规范化的数据库,但是那种想要对规范进行非规范化的人通常在设置索引时就不会考虑基数。

重要的是要了解,表扫描(在生成联接的过程中检查表中的每一行)在实践中很少见。仅当以下一项或多项成立时,查询优化器才会选择表扫描。

  • 关系中少于200行(在这种情况下,扫描会便宜一些)
  • 连接列上没有合适的索引(如果在这些列上连接有意义,那么为什么不对它们建立索引呢?修复它)
  • 列可以进行比较(WTF?修复它,要么回家)前的类型强制要求见底笔记ADO.NET发行
  • 比较的参数之一是表达式(无索引)

执行一项操作要比不执行一项操作花费更多。但是,执行错误的操作(被迫进入无意义的磁盘I / O,然后在执行您真正需要的连接之前丢弃浮渣)昂贵得多。即使预先计算了“错误”的操作并且明智地应用了索引,仍然存在很大的代价。尽管需要进行更新异常,但通过非规范化来预先计算联接是对特定联接的承诺。如果您需要其他联接,那么该承诺将使您付出巨大的代价。

如果有人想提醒我这是一个不断变化的世界,我想您会发现,gruntier硬件上的更大数据集只会夸大Date的发现之广。

对于所有使用计费系统或垃圾邮件生成器(感到羞耻)并且愤慨地动手写键盘的人,告诉我您知道反规范化速度更快的事实,对不起,您生活在一个特殊的群体中。案例-具体来说是按顺序处理所有数据的案例。这不是一般情况,您的策略合理的。

没有理由对它进行错误的概括。有关在数据仓库场景中适当使用非规范化的更多信息,请参见注释部分的结尾。

我也想回应

联接只是具有一些唇彩的笛卡尔积

多么胡扯。限制要尽早应用,首先要严格限制。您已阅读该理论,但尚未理解。查询优化器将联接视为 “谓词适用的笛卡尔积” 。这是一种符号表示形式(实际上是一种归一化),有助于符号分解,因此优化程序可以生成所有等效转换,并按成本和选择性对它们进行排名,以便可以选择最佳查询计划。

获得生产笛卡尔乘积的优化器的唯一方法是不提供谓词: SELECT * FROM A,B


笔记


David Aldridge提供了一些重要的附加信息。

实际上,除了索引和表扫描之外,还有许多其他策略,而现代的优化器将在制定执行计划之前将它们全部花费。

一条实用的建议:如果可以将其用作外键,则对其进行索引,以便优化器可以使用索引策略。

我曾经比MSSQL优化器更聪明。那在两个版本之前改变了。现在它一般会教给我。实际上,它是一个专家系统,可以将许多非常聪明的人的所有智慧汇集到一个足够封闭的领域中,从而使基于规则的系统有效。


“ Blocks”可能没有任何技巧。我被要求不要那么傲慢,并提醒我数学不是在撒谎。的确如此,但数学模型的所有含义不一定都应从字面上理解。如果您仔细地避免检查它们的荒谬性(双关),并确保在尝试解释方程式之前先将其全部抵消,则负数的平方根非常有用。

我之所以如此大胆地回应的原因是,措词如下:

联接笛卡尔积...

这可能不是什么意思,但是它写的,而且绝对是错误的。笛卡尔积是一个关系。联接是一种功能。更具体地说,联接是一种关系值函数。如果谓词为空,则将生成笛卡尔乘积,并且对其进行检查是对数据库查询引擎的一次正确性检查,但实际上没有人编写不受约束的联接,因为它们在课堂外没有任何实用价值。

我之所以这样称呼,是因为我不希望读者陷入将模型与建模对象混淆的古老陷阱。模型是一个近似值,为方便操作而特意简化。


在不同数据库引擎之间,选择表扫描联接策略的临界值可能会有所不同。它受许多实现决策的影响,例如,树节点填充因子,键值大小和算法的精妙之处,但从广义上讲,高性能索引的执行时间为k log n + c。C项是固定的开销,主要由建立时间组成,曲线的形状意味着直到n达到数百时,您才能获得回报(与线性搜索相比)。


有时非正规化是个好主意

非规范化是对特定联接策略的承诺。如前所述,这会干扰其他联接策略。但是,如果您拥有大量的磁盘空间,可预测的访问模式以及倾向于处理很多或全部磁盘的趋势,那么预先计算联接可能非常值得。

您还可以找出您的操作通常使用的访问路径,并预先计算这些访问路径的所有联接。这是数据仓库背后的前提,或者至少是在由知道自己为什么要做自己的事情的人建造它们的时候,而不仅仅是为了遵守流行语。

经过适当设计的数据仓库会通过从规范化交易处理系统中进行批量转换而定期生成。操作和报告数据库的这种分离具有消除OLTP和OLAP(在线事务处理即数据输入和在线分析处理即报告)之间的冲突的非常理想的效果。

这里重要的一点是,除了定期更新之外,数据仓库是只读的。这就提出了更新异常的问题。

不要犯对OLTP数据库(进行数据输入的数据库)进行规范化的错误。计费运行可能会更快,但是如果这样做,则会出现更新异常。有没有试图让《读者文摘》停止向您发送东西?

这些天磁盘空间很便宜,所以请自行解决。但是非规范化只是数据仓库的一部分。预先计算的汇总值可带来更大的性能提升:每月总计,诸如此类。它总是约降低了工作组。


类型不匹配的ADO.NET问题

假设您有一个SQL Server表,其中包含varchar类型的索引列,并使用AddWithValue传递参数来约束对该列的查询。C#字符串是Unicode,因此推断的参数类型将是NVARCHAR,与VARCHAR不匹配。

从VARCHAR到NVARCHAR的转换是一个扩大的转换,因此它隐式发生-但是,告别索引,祝您好运。


“计算磁盘命中率”(Rick James)

如果所有内容都缓存在RAM中,那就JOINs便宜了。也就是说,归一化不会带来太多的性能损失

如果“规范化”模式导致JOINs大量磁盘损坏,但是等效的“非规范化”模式则不必击中磁盘,那么非规范化将赢得性能竞争。

原始作者的评论:现代数据库引擎非常擅长组织访问排序,以最大程度地减少联接操作期间的缓存丢失。上面的虽然是正确的,但可能会被误解为暗示在大数据上连接必定会带来问题,代价是昂贵的。这将导致经验不足的开发人员做出错误的决策。


7
这些语句的子句特定于特定的DBMS,不是吗?例如。“关系中少于200行”
David Aldridge

2
使用(或不使用)替代键是否会对所有这些产生重大影响?
David Plumpton,2009年

3
伟大的EF Codd全权负责关系模型。CJ Date和最近的H Darwen都是白痴,他们不理解RM,并且提供了大量有关“如何改进” RM的信息,所有这些信息都可以被忽略,因为一个人无法解决一个不理解的问题。通过暗示存在“缺失”,它们仅用于损害RM的相关性。
PerformanceDBA

7
同样,不要忘记,许多NoSQL数据库本质上是我们40年前丢弃的数据库。年轻人总是以为自己发现了一些新东西。Fabian Pascal:dbdebunk.com/2014/02/thinking-logically-sql-nosql-and.html
N West

3
好斗。这是一个很好的说明,但是侵略性和微观侵略性并没有增加内容或内容的价值。
MrMesees'6

46

大多数评论者没有注意到的是,复杂的RDBMS中可用的连接方法范围很广,而反规范化器总是掩盖了维护非规范化数据的较高成本。并非每个联接都基于索引,并且数据库具有许多优化的联接算法和方法,旨在降低联接成本。

无论如何,联接的成本取决于联接的类型和其他一些因素。它根本不需要昂贵-一些例子。

  • 实际上,散装数据是等价的散列联接确实非常便宜,并且仅当无法将散列表存储在内存中时,开销才显着。不需要索引。联接的数据集之间的均等分区可以提供很大的帮助。
  • 排序合并联接的成本是由排序的成本而非合并驱动的-基于索引的访问方法实际上可以消除排序的成本。
  • 索引上的嵌套循环联接的成本由b树索引的高度和对表块本身的访问来驱动。它速度很快,但不适用于批量连接。
  • 基于集群的嵌套循环连接要便宜得多,每条连接行所需的逻辑AL IO更少-如果连接的表都在同一集群中,则通过并置连接的行,连接将变得非常便宜。

数据库是设计用于联接的,它们在执行操作时非常灵活,并且通常非常高效,除非它们弄错了联接机制。


我认为可以归结为“如有疑问,请咨询您的DBA”。现代数据库是复杂的野兽,确实需要学习才能理解。自1996年以来,我才一直使用Oracle,并且这是一项全职工作,以跟上这些新功能。自2005年以来,SQLserver也取得了长足的进步。这不是一个黑匣子!
Guy

2
嗯,以我的拙劣经验来说,有太多的DBA从未听说过哈希联接,或者认为它们是普遍性的事情。
David Aldridge

28

我认为整个问题都基于错误的前提。加入大表是不是要贵。实际上,高效地进行连接是关系数据库根本存在的主要原因之一。大集合上的联接通常很昂贵,但是很少有人想要将大表A的全部内容与大表B的全部内容联接在一起。相反,您编写查询时只使用了每个表的重要行,并且联接保留的实际集合仍然较小。

此外,您还具有Peter Wone提到的效率,因此只有每个记录的重要部分都需要存储在内存中,直到最终结果集实现为止。此外,在具有许多联接的大型查询中,您通常希望从较小的表集开始,然后逐步处理较大的表集,以便保留在内存中的集合尽可能长地保持尽可能短的时间。

正确完成后,联接通常是比较,合并或过滤大量数据的最佳方法


1
@joel。反之亦然。大型数据集联接可能很昂贵,有时甚至是必需的,但是您不希望这样做太频繁,除非a)可以处理所需的IO和RAM,b)不必经常这样做。考虑实例化视图,报告系统,实时与CoB报告。
Guy

11

瓶颈几乎总是磁盘I / O,更具体地说-随机磁盘I / O(相比之下,顺序读取的速度相当快,并且可以使用预读策略进行缓存)。

如果您要四处阅读大型表的小部分,则联接可以增加随机查找的次数。但是,如果查询优化器认为这样做会更好,则会将其转换为顺序表扫描(丢弃不需要的行)。

单个非规范化表有一个类似的问题-行很大,因此不太适合单个数据页。如果您需要的行与另一行之间的距离很远(并且较大的行大小使它们彼此分开),那么您将拥有更多的随机I / O。同样,可能会强制进行表扫描以避免这种情况。但是,这一次,由于行较大,因此表扫描必须读取更多数据。此外,您还需要将数据从一个位置复制到多个位置,而RDBMS具有更多的读取(和缓存)内容。

使用2个表,您还将获得2个聚集索引-并且通常可以索引更多(由于较少的插入/更新开销),从而可以显着提高性能(主要是,由于索引相对较小,因此可以快速读取磁盘) (或便宜的缓存),并减少了您需要从磁盘读取的表行的数量)。

找出联接的唯一开销来自找出匹配的行。Sql Server主要根据数据集大小使用3种不同类型的联接来查找匹配的行。如果优化器选择了错误的联接类型(由于统计信息不正确,索引不足,或者仅仅是优化器错误或边缘情况),则可能会严重影响查询时间。

  • 循环连接对于(至少1个)小型数据集来说非常便宜。
  • 合并联接首先需要两种数据集。但是,如果您连接到索引列,则该索引已经排序,无需进行进一步的工作。否则,排序会占用一些CPU和内存。
  • 哈希联接需要内存(用于存储哈希表)和CPU(用于构建哈希)。同样,相对于磁盘I / O,这是相当快的。但是,如果没有足够的RAM存储哈希表,则Sql Server将使用tempdb存储哈希表的部分内容和找到的行,然后一次仅处理哈希表的部分内容。与所有磁盘一样,这相当慢。

在最佳情况下,这些不会导致磁盘I / O-因此从性能角度来看可以忽略不计。

总而言之,最糟糕的是-读取相同数量的逻辑实际上应该更快从磁盘读取的磁盘较小,从x个联接的表中数据从单个非规范化表中数据。要读取相同数量的物理数据,可能会有一些开销。

由于查询时间通常由I / O成本决定,并且数据的大小不会因非规范化而改变(减去一些很小的行开销),因此仅将表合并在一起并不会带来很多好处。趋于提高性能的非规范化类型(IME)会缓存计算的值,而不是读取计算它们所需的10,000行。


减少随机寻道:很好,尽管具有良好缓存的RAID控制器可以进行电梯读/写。
Peter Wone

3

您加入表的顺序非常重要。如果您有两组数据,请尝试以某种方式构建查询,以便首先使用最小的数据集来减少查询必须处理的数据量。

对于某些数据库而言,这并不重要,例如,MS SQL多数时候确实知道正确的连接顺序。对于某些产品(例如IBM Informix),顺序会有所不同。


1
通常,一个体面的查询优化器将不受联接或表列出的顺序的影响,并将自行决定执行联接的最有效方式。
David Aldridge

5
MySQL,Oracle,SQL Server,Sybase,postgreSQL等 不在乎连接顺序。我已经使用过DB2,据我所知,它也不关心您输入的顺序。在一般情况下,这不是有用的建议
Matt Rogish

使用NDB引擎的MySQL集群(很可能是一个极端情况,只有高级开发人员会接近NDB)无法正确猜出连接顺序,因此您必须向大多数已加入的查询中添加“ USE INDEX”语句,否则它们会效率极低。MySQL文档对此进行了介绍。
joelhardi

@iiya,了解优化程序将选择什么比与表排序有关的广义陈述或“神话”更为重要。不要依赖SQL中的特定问题,因为升级RDBMS时,行为通常会改变。自v7以来,Oracle已经多次更改了行为。
Guy

1
@Matt我已经看到Oracle 9i进行了非常不同的优化,查询计划只是调整了连接顺序。也许从10i版本起已经改变了?
卡米洛·迪亚斯·雷普卡

0

当考虑联接的复杂性类时,决定是进行非规范化还是规范化是一个相当简单的过程。例如,当查询为O(k log n)时,我倾向于使用归一化设计数据库,其中k相对于所需的输出幅度。

一种使规范化和优化性能的简单方法是考虑对规范化结构的更改如何影响您的规范化结构。但是,这可能会带来问题,因为它可能需要事务逻辑才能在非规范化结构上工作。

关于标准化和非标准化的争论不会结束,因为问题很大。自然解决方案需要两种方法都存在很多问题。

通常,我一直存储着规范化的结构和可以重构的非规范化缓存。最终,这些缓存节省了我的钱来解决将来的规范化问题。


-8

细说别人的话,

联接只是具有一些唇彩的笛卡尔积。{1,2,3,4} X {1,2,3}将给我们12个组合(nXn = n ^ 2)。此计算集用作应用条件的参考。DBMS应用条件(例如左右两个均为2或3的条件)来为我们提供匹配条件。实际上,它是经过优化的,但问题是相同的。集合大小的更改将成倍增加结果大小。消耗的内存量和cpu周期都以指数方式影响。

当我们进行非规范化处理时,我们完全避免了这种计算,而是考虑在书的每一页上都贴上彩色的粘纸。您可以不使用参考来推断信息。我们付出的代价是我们正在损害DBMS的本质(数据的最佳组织)


3
-1:这篇文章很好地说明了为什么让DBMS执行联接-因为DBMS设计人员一直在考虑这些问题,并且提出了比compsci 101方法更有效的方法。
大卫·奥尔德里奇

2
@大卫:同意。DBMS优化器程序员是一些智能Cookie
Matt Rogish'Oct

这个答案是不正确的。如果您的查询是针对规范化的索引数据库执行的,并且具有任何类型的过滤器或联接条件,则优化器将找到一种避免笛卡尔积的方法,并最大程度地减少内存使用量和CPU周期。如果您实际上打算选择笛卡尔乘积,则将在归一化或非归一化数据库中使用相同的内存。
rileymcdowell,
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.