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



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

我知道大桌子的连接非常昂贵,但我不完全确定为什么。DBMS需要做什么来执行联接操作,瓶颈在哪里
非规范化如何帮助克服这一开销?其他优化技术(例如索引)有什么帮助?

欢迎个人体验!如果你要发布资源链接,请避开维基百科。我已经知道在哪里可以找到了。

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

去标准化以提高性能?这听起来很有说服力,但站不住脚。

Chris Date与Ted Codd博士是关系数据模型的最初支持者,他对反对规范化的错误论点失去了耐心,并使用科学方法系统地摧毁了这些论点:他获得了大型数据库,并测试了这些断言。

我想他在《1988-1991年关系数据库写作》中写过这本书,但这本书后来被卷进了《数据库系统导论》的第六版,这本书是关于数据库理论和设计的权威性文本,在我写的第八版中,很可能会在未来几十年里继续出版。当我们大多数人还光着脚到处跑的时候,Chris Date就是这个领域的专家。

他发现:

  • 其中一些适用于特殊情况
  • 所有这些都没有得到普遍使用的回报
  • 对于其他特殊情况,所有这些情况都明显更糟

这一切都归结为减轻工作集的大小。包含正确选择的键和正确设置的索引的联接是廉价而不昂贵的,因为它们允许在行实现之前对结果进行显著的修剪。

将结果具体化涉及到大容量磁盘读取,这是该练习中最昂贵的一个数量级。相比之下,执行联接在逻辑上只需要检索。在实践中,甚至没有提取键值:键哈希值用于联接比较,从而降低多列联接的成本,并从根本上降低涉及字符串比较的联接的成本。不仅更适合缓存,而且要做的磁盘读取也更少

此外,一个好的优化器会选择最严格的条件,并在执行联接之前应用它,非常有效地利用了具有高基数的索引上联接的高选择性。

诚然,这种类型的优化也可以应用于去规范化的数据库,但希望去规范化模式的人在设置索引时通常不会考虑基数。

重要的是要理解,表扫描(在生成联接的过程中检查表中的每一行)在实践中是罕见的。只有当以下一个或多个条件成立时,查询优化器才会选择表扫描。

  • 关系中少于200行(在这种情况下,扫描会更便宜)
  • 联接列上没有合适的索引(如果在这些列上联接有意义,那么为什么不对它们进行索引?修复它)
  • 在比较列之前需要类型强制(WTF?!修复它或返回主页)请参阅ADO的结束注释。NET问题
  • 比较的参数之一是表达式(无索引)

执行操作比不执行操作更昂贵。但是,执行错误的操作,被迫进行毫无意义的磁盘I/O,然后在执行真正需要的联接之前丢弃糟粕,成本要高得多。即使预先计算了"错误"的操作,并且合理地应用了索引,仍然会受到很大的惩罚。去规范化预计算联接——尽管会带来更新异常——是对特定联接的承诺。如果你需要一个不同的加入,这个承诺会让你付出巨大的代价。

如果有人想提醒我,这是一个不断变化的世界,我想你会发现,在更笨重的硬件上更大的数据集只会夸大Date发现的传播。

对于所有在计费系统或垃圾邮件生成器上工作的人(真是太遗憾了),他们愤怒地用手对键盘告诉我,你知道去规范化更快,很抱歉,但你生活在一种特殊的情况下,特别是你按顺序处理所有数据的情况。这不是一般情况,您的策略是合理的。

您错误地概括它是不合理的。有关在数据仓库场景中适当使用去规范化的更多信息,请参阅注释部分的末尾。

我也想回复

Joins只是带有一些唇彩的笛卡尔产品

真是胡说八道。限制措施应尽早实施,最严格的限制措施应首先实施。您已经阅读了该理论,但还没有理解它。查询优化器仅将联接视为"谓词应用的笛卡尔乘积"。这是一种符号表示(事实上是一种规范化),以便于符号分解,因此优化器可以生成所有等效变换,并根据成本和选择性对其进行排序,从而

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

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

  • 对大容量数据进行等连接的哈希连接确实非常便宜,而且只有当哈希表不能缓存在内存中时,成本才会变得很大。不需要索引。在连接的数据集之间进行相等的分区会有很大的帮助
  • 排序合并联接的成本是由排序成本而不是合并成本驱动的——基于索引的访问方法实际上可以消除排序成本
  • 索引上嵌套循环联接的成本由b-树索引的高度和表块本身的访问量驱动。它速度很快,但不适合批量连接
  • 基于集群的嵌套循环连接要便宜得多,每个连接行所需的逻辑IO更少——如果连接的表都在同一集群中,那么通过连接行的主机代管,连接变得非常便宜

数据库是为连接而设计的,它们的连接方式非常灵活,除非连接机制出错,否则通常性能非常好。

我认为整个问题是基于一个错误的前提。大型表上的联接并不一定很昂贵。事实上,高效地执行联接是关系数据库存在的主要原因之一。大型集合上的联接通常很昂贵,但您很少希望将大型表A的整个内容与大型表B的整个内容联接。相反,您编写查询时,只使用每个表的重要行,而联接保留的实际集合保持较小。

此外,您还有Peter Wone提到的效率,因此在最终结果集具体化之前,每个记录的重要部分只需要在内存中。此外,在具有多个联接的大型查询中,您通常希望从较小的表集开始,然后逐步扩展到较大的表集,这样存储在内存中的表集就可以尽可能长时间地保持较小。

如果操作得当,联接通常是对大量数据进行比较、组合或筛选的最佳方式

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

联接可以增加随机查找-如果你在阅读大表的小部分。但是,查询优化器会寻找它,并将其转换为顺序表扫描(丢弃不需要的行),如果它认为这会更好的话。

单个非规范化表也有类似的问题——行很大,因此不太适合单个数据页。如果您需要相距较远的行(而较大的行大小使它们相距更远),那么您将拥有更多的随机I/O。同样,为了避免这种情况,可以强制进行表扫描。但是,这一次,由于行大小较大,您的表扫描必须读取更多数据。再加上将数据从一个位置复制到多个位置,RDBMS就有更多的数据要读取(和缓存)。

对于2个表,您还可以获得2个聚集索引,并且通常可以索引更多(因为插入/更新开销较小),这可以显著提高性能(主要是因为索引(相对)小,从磁盘读取速度快(或缓存成本低),并减少需要从磁盘读取的表行数)。

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

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

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

总而言之,在最坏的情况下,从x个联接表中读取相同数量的逻辑数据实际上应该更快,因为磁盘读取量较小,所以从单个非规范化表中读取的数据应该更快。要读取相同数量的物理数据,可能会有一些轻微的开销。

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

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

对于某些数据库来说,这并不重要,例如MSSQL在大多数情况下都知道正确的联接顺序。对于一些人(比如IBMInformix)来说,订单起到了很大的作用。

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

反规范化和优化性能的一个简单方法是考虑对规范化结构的更改如何影响反规范化结构。然而,它可能会有问题,因为它可能需要事务逻辑来处理非规范化的结构化。

关于规范化和非规范化的争论不会结束,因为问题是巨大的。在许多问题中,自然解决方案需要两种方法。

一般来说,我总是存储可以重构的规范化结构和非规范化缓存。最终,这些缓存节省了我解决未来规范化问题的时间。

详述他人的言论,

连接只是带有一些唇彩的笛卡尔乘积s. {1,2,3,4}X{1,2,3}将给我们12个组合(nXn=n^2)。该计算集用作应用条件的参考。DBMS应用这些条件(比如左和右都是2或3)来给我们匹配的条件。事实上,它更优化了,但问题是一样的。集合大小的变化将使结果大小呈指数级增加。消耗的内存量和cpu周期都是以指数形式影响的。

当我们去规范化时,我们完全避免了这种计算,想象一下在你的书的每一页上都贴上一个彩色的粘性。您可以使用引用推断out的信息。我们付出的代价是,我们正在损害DBMS(数据的最佳组织)的本质

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

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

我知道大桌子的连接非常昂贵,但我不完全确定为什么。DBMS需要做什么来执行联接操作,瓶颈在哪里
非规范化如何帮助克服这一开销?其他优化技术(例如索引)有什么帮助?

欢迎个人体验!如果你要发布资源链接,请避开维基百科。我已经知道在哪里可以找到了。

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

去标准化以提高性能?这听起来很有说服力,但站不住脚。

Chris Date与Ted Codd博士是关系数据模型的最初支持者,他对反对规范化的错误论点失去了耐心,并使用科学方法系统地摧毁了这些论点:他获得了大型数据库,并测试了这些断言。

我想他在《1988-1991年关系数据库写作》中写过这本书,但这本书后来被卷进了《数据库系统导论》的第六版,这本书是关于数据库理论和设计的权威性文本,在我写的第八版中,很可能会在未来几十年里继续出版。当我们大多数人还光着脚到处跑的时候,Chris Date就是这个领域的专家。

他发现:

  • 其中一些适用于特殊情况
  • 所有这些都没有得到普遍使用的回报
  • 对于其他特殊情况,所有这些情况都明显更糟

这一切都归结为减轻工作集的大小。包含正确选择的键和正确设置的索引的联接是廉价而不昂贵的,因为它们允许在行实现之前对结果进行显著的修剪。

将结果具体化涉及到大容量磁盘读取,这是该练习中最昂贵的一个数量级。相比之下,执行联接在逻辑上只需要检索。在实践中,甚至没有提取键值:键哈希值用于联接比较,从而降低多列联接的成本,并从根本上降低涉及字符串比较的联接的成本。不仅更适合缓存,而且要做的磁盘读取也更少

此外,一个好的优化器会选择最严格的条件,并在执行联接之前应用它,非常有效地利用了具有高基数的索引上联接的高选择性。

诚然,这种类型的优化也可以应用于去规范化的数据库,但希望去规范化模式的人在设置索引时通常不会考虑基数。

重要的是要理解,表扫描(在生成联接的过程中检查表中的每一行)在实践中是罕见的。只有当以下一个或多个条件成立时,查询优化器才会选择表扫描。

  • 关系中少于200行(在这种情况下,扫描会更便宜)
  • 联接列上没有合适的索引(如果在这些列上联接有意义,那么为什么不对它们进行索引?修复它)
  • 在比较列之前需要类型强制(WTF?!修复它或返回主页)请参阅ADO.NET ISSUE的结束注释
  • 比较的参数之一是表达式(无索引)

执行操作比不执行操作更昂贵。但是,执行错误的操作,被迫进行毫无意义的磁盘I/O,然后在执行真正需要的联接之前丢弃糟粕,成本要高得多。即使预先计算了"错误"的操作,并且合理地应用了索引,仍然会受到很大的惩罚。去规范化预计算联接——尽管会带来更新异常——是对特定联接的承诺。如果你需要一个不同的加入,这个承诺会让你付出巨大的代价。

如果有人想提醒我,这是一个不断变化的世界,我想你会发现,在更笨重的硬件上更大的数据集只会夸大Date发现的传播。

对于所有在计费系统或垃圾邮件生成器上工作的人(真是太遗憾了),他们愤怒地用手对键盘告诉我,你知道去规范化更快,很抱歉,但你生活在一种特殊的情况下,特别是你按顺序处理所有数据的情况。这不是一般情况,您的策略是合理的。

您错误地概括它是不合理的。有关在数据仓库场景中适当使用去规范化的更多信息,请参阅注释部分的末尾。

我也想回复

Joins只是带有一些唇彩的笛卡尔产品

真是胡说八道。限制措施应尽早实施,最严格的限制措施应首先实施。您已经阅读了该理论,但还没有理解它。查询优化器仅将联接视为"谓词应用的笛卡尔乘积"。这是一种符号表示(事实上是一种规范化),用于促进符号分解,因此优化器可以生成所有等效的转换,并根据成本和选择性对其进行排序,以便选择最佳查询计划。

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


注释


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

除了索引和表扫描之外,确实还有各种其他策略,现代优化器在制定执行计划之前会花费所有这些策略。

一条实用的建议:如果它可以用作外键,那么对它进行索引,这样优化器就可以使用索引策略。

我曾经比MSSQL优化器更聪明。这在两个版本前发生了变化。现在它通常教。从一个非常真实的意义上讲,它是一个专家系统,将许多非常聪明的人的所有智慧汇集在一个足够封闭的领域中,从而使基于规则的系统有效。


"Bollocks"可能是不得体的。有人要求我不要那么傲慢,并提醒我数学不会说谎。这是真的,但并非所有数学模型的含义都必须从字面上理解。如果你小心地避免检查负数的平方根的荒谬性(这里是双关语),并确保在你试图解释你的方程之前把它们全部消除,那么负数的平方根就非常方便了。

我之所以如此野蛮地回应,是因为声明的措辞表明

Joins笛卡尔乘积。。。

这可能不是本意,但就是所写的,这绝对是不真实的。笛卡尔乘积是一种关系。联接是一个函数。更具体地说,联接是一个关系值函数。对于空谓词,它将生成笛卡尔乘积,检查它是否这样做是对数据库查询引擎的一种正确性检查,但在实践中没有人编写无约束联接,因为它们在课堂之外没有实际价值。

我之所以这么说,是因为我不想让读者陷入将模型与模型混淆的古老陷阱。模型是一种近似值,为了便于操作而故意简化。


选择表扫描联接策略的截止值可能因数据库引擎而异。它受到许多实现决策的影响,如树节点填充因子、键值大小和算法的微妙性,但从广义上讲,高性能索引的执行时间为klogn+c。C项是一个固定的开销,主要由设置时间组成,曲线的形状意味着直到n达到数百,你才能得到回报(与线性搜索相比)。


有时去规范化是个好主意

去规范化是对特定加入策略的承诺。如前所述,这会干扰其他加入策略。但是,如果您有大量的磁盘空间、可预测的访问模式,并且倾向于处理大部分或全部磁盘空间,那么预计算联接可能是非常有价值的。

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

通过对规范化事务处理系统的批量转换,定期生成设计正确的数据仓库。操作和报告数据库的这种分离具有非常理想的效果,可以消除OLTP和OLAP(在线事务处理即数据输入和在线分析处理即报告)之间的冲突。

这里的一个重要点是,除了定期更新之外,数据仓库是只读的。这使得更新异常的问题变得毫无意义。

不要犯将OLTP数据库(数据输入所在的数据库)去规范化的错误。计费运行可能会更快,但如果你这样做,你会发现更新异常。有没有试过让《读者文摘》停止给你寄东西?

现在磁盘空间很便宜,所以请尽情享受吧。但对于数据仓库来说,去规范化只是故事的一部分。更大的性能提升来自于预先计算的汇总值:每月总计,诸如此类的东西。总是关于减少工作集。


类型不匹配的ADO.NET问题

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

VARCHAR到NVARCHAR是一个不断扩大的转换,所以它是隐式发生的——但告别索引,祝你好运,找出原因。


"计算磁盘命中率"(里克·詹姆斯)

如果所有内容都缓存在RAM中,那么JOINs相当便宜。也就是说,规范化没有太多性能损失

如果"规范化"模式导致JOINs大量访问磁盘,但等效的"非规范化"架构不必访问磁盘,那么非规范化将赢得性能竞争。

原作者评论:现代数据库引擎非常善于组织访问顺序,以最大限度地减少联接操作期间的缓存未命中。上述情况虽然正确,但可能会被误解为意味着连接在大数据上必然会出现昂贵的问题。这将导致缺乏经验的开发人员决策失误。

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

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

  • 对大容量数据进行等连接的哈希连接确实非常便宜,而且只有当哈希表不能缓存在内存中时,成本才会变得很大。不需要索引。在连接的数据集之间进行相等的分区会有很大的帮助
  • 排序合并联接的成本是由排序成本而不是合并成本驱动的——基于索引的访问方法实际上可以消除排序成本
  • 索引上嵌套循环联接的成本由b-树索引的高度和表块本身的访问量驱动。它速度很快,但不适合批量连接
  • 基于集群的嵌套循环连接要便宜得多,每个连接行所需的逻辑IO更少——如果连接的表都在同一集群中,那么通过连接行的主机代管,连接变得非常便宜

数据库是为连接而设计的,它们的连接方式非常灵活,除非连接机制出错,否则通常性能非常好。

我认为整个问题是基于一个错误的前提。大型表上的联接并不一定很昂贵。事实上,高效地执行联接是关系数据库存在的主要原因之一。大型集合上的联接通常很昂贵,但您很少希望将大型表A的整个内容与大型表B的整个内容联接。相反,您编写查询时,只使用每个表的重要行,而联接保留的实际集合保持较小。

此外,您还有Peter Wone提到的效率,因此在最终结果集具体化之前,每个记录的重要部分只需要在内存中。此外,在具有多个联接的大型查询中,您通常希望从较小的表集开始,然后逐步扩展到较大的表集,这样存储在内存中的表集就可以尽可能长时间地保持较小。

如果操作得当,联接通常是对大量数据进行比较、组合或筛选的最佳方式

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

联接可以增加随机查找-如果你在阅读大表的小部分。但是,查询优化器会寻找它,并将其转换为顺序表扫描(丢弃不需要的行),如果它认为这会更好的话。

单个非规范化表也有类似的问题——行很大,因此不太适合单个数据页。如果您需要相距较远的行(而较大的行大小使它们相距更远),那么您将拥有更多的随机I/O。同样,为了避免这种情况,可以强制进行表扫描。但是,这一次,由于行大小较大,您的表扫描必须读取更多数据。再加上将数据从一个位置复制到多个位置,RDBMS就有更多的数据要读取(和缓存)。

对于2个表,您还可以获得2个聚集索引,并且通常可以索引更多(因为插入/更新开销较小),这可以显著提高性能(主要是因为索引(相对)小,从磁盘读取速度快(或缓存成本低),并减少需要从磁盘读取的表行数)。

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

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

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

总而言之,在最坏的情况下,从x个联接表中读取相同数量的逻辑数据实际上应该更快,因为磁盘读取量较小,所以从单个非规范化表中读取的数据应该更快。要读取相同数量的物理数据,可能会有一些轻微的开销。

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

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

对于某些数据库来说,这并不重要,例如MSSQL在大多数情况下都知道正确的联接顺序。对于一些人(比如IBMInformix)来说,订单起到了很大的作用。

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

反规范化和优化性能的一个简单方法是考虑对规范化结构的更改如何影响反规范化结构。然而,它可能会有问题,因为它可能需要事务逻辑来处理非规范化的结构化。

关于规范化和非规范化的争论不会结束,因为问题是巨大的。在许多问题中,自然解决方案需要两种方法。

一般来说,我总是存储可以重构的规范化结构和非规范化缓存。最终,这些缓存节省了我解决未来规范化问题的时间。

详述他人的言论,

连接只是带有一些唇彩的笛卡尔乘积s. {1,2,3,4}X{1,2,3}将给我们12个组合(nXn=n^2)。该计算集用作应用条件的参考。DBMS应用这些条件(比如左和右都是2或3)来给我们匹配的条件。事实上,它更优化了,但问题是一样的。集合大小的变化将使结果大小呈指数级增加。消耗的内存量和cpu周期都是以指数形式影响的。

当我们去规范化时,我们完全避免了这种计算,想象一下在你的书的每一页上都贴上一个彩色的粘性。您可以使用引用推断out的信息。我们付出的代价是,我们正在损害DBMS(数据的最佳组织)的本质

最新更新