小编典典

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

all

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

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

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


阅读 70

收藏
2022-03-28

共1个答案

小编典典

反规范化以提高性能?这听起来很有说服力,但它不成立。

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

我认为他在 Relational Database Writings 1988-1991 中写了这本书,但这本书后来被纳入 数据库系统简介的
第六版,这是关于数据库理论和设计 权威书籍,在我写的第八版中并且可能会保留印刷数十年。当我们大多数人还赤脚跑来跑去的时候,Chris Date
是这个领域的专家。

他发现:

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

这一切都归结为减少工作集的大小。涉及正确选择的键和正确设置的索引的连接很便宜,而不是昂贵,因为它们允许在行实现 之前对结果进行大量修剪。

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

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

诚然,这种类型的优化也可以应用于非规范化的数据库,但是那些 想要 非规范化模式的人通常不会在(如果)设置索引时考虑基数。

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

  • 关系中的行数少于 200(在这种情况下,扫描会更便宜)
  • 连接列上没有合适的索引(如果在这些列上连接有意义,那么为什么它们不被索引?修复它)
  • 在可以比较列之前需要类型强制(WTF?!修复它或回家) 查看 ADO.NET 问题的尾注
  • 比较的参数之一是表达式(无索引)

执行一个操作比不执行它更昂贵。但是,执行 错误 的操作,被迫进行无意义的磁盘 I/O,然后在执行您真正需要的连接之前丢弃渣滓,代价要高 得多。
即使预先计算了“错误”操作并且已合理应用索引,仍然存在重大损失。预先计算连接的非规范化 - 尽管需要更新异常 - 是对特定连接的承诺。如果您需要 不同
的加入,那么该承诺将使您付出 巨大 的代价。

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

对于所有在计费系统或垃圾邮件生成器上工作的人(为你感到羞耻)并且愤怒地把手放在键盘上告诉我你知道非规范化更快的事实,对不起,但你生活在一个特别的地方案例 -
特别是您按顺序处理 所有 数据的情况。这不是一般情况,您的策略 合理的。

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

我也想回复

连接只是带有一些唇彩的笛卡尔积

真是一堆废话。尽可能早地应用限制,首先应用最严格的限制。你读过这个理论,但你还没有理解它。连接 被查询优化器 视为 “应用谓词的笛卡尔积”
。这是一种符号表示(实际上是规范化),以促进符号分解,因此优化器可以生成所有等效转换并按成本和选择性对它们进行排序,以便选择最佳查询计划。 __

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


笔记


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

除了索引和表扫描之外,确实还有很多其他策略,现代优化器会在生成执行计划之前将它们全部消耗掉。

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

我曾经比 MSSQL 优化器更聪明。这在两个版本前改变了。现在它一般教
。在非常真实的意义上,它是一个专家系统,将许多非常聪明的人的所有智慧都编入了一个足够封闭的领域,以至于基于规则的系统是有效的。


“胡说八道”可能是不圆滑的。我被要求不要那么傲慢,并提醒我数学不会说谎。这是真的,但并非数学模型的所有含义都必须按字面意思理解。如果您仔细避免检查它们的荒谬性(双关语)并确保在尝试解释方程之前将它们全部取消,则负数的平方根非常方便。

我如此野蛮地回应的原因是措辞上的声明说

联接 笛卡尔积…

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

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


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


有时非规范化是个好主意

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

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

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

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

不要犯非规范化 OLTP 数据库(发生数据输入的数据库)的错误。计费运行可能会更快,但如果您这样做,您将收到更新异常。曾经试图让读者文摘停止向你发送东西吗?

这些天磁盘空间很便宜,所以把自己搞砸了。但非规范化只是数据仓库故事的一部分。更大的性能提升来自预先计算的汇总值:每月总计,诸如此类。它 总是
关于减少工作集。


类型不匹配的 ADO.NET 问题

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

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


“计算磁盘命中数”(里克·詹姆斯)

如果一切都缓存在 RAM 中,JOINs则相当便宜。也就是说,归一化并没有太大的 性能损失

如果“规范化”模式导致JOINs大量磁盘命中,但等效的“非规范化”模式不必命中磁盘,则非规范化赢得了性能竞争。

原作者评论:现代数据库引擎非常擅长组织访问排序,以最大限度地减少连接操作期间的缓存未命中。上述内容虽然是正确的,但可能会被误解为暗示连接在大数据上必然存在问题昂贵。这将导致缺乏经验的开发人员做出糟糕的决策。

2022-03-28