考虑可扩展性时,为什么连接会变坏?

89 sql join

为什么加入不好或'慢'.我知道我多听过一次.我找到了这个引用

问题是连接速度相对较慢,特别是在非常大的数据集上,如果它们很慢,那么您的网站速度很慢.从磁盘上获取所有这些独立的信息并将它们全部放在一起需要很长时间.

资源

我一直认为他们特别快,特别是在查找PK时.他们为什么"慢"?

Joe*_*orn 91

可扩展性就是预先计算,扩展或削减重复工作到最基本的要素.为了更好地扩展,你不需要做大量不需要的事情,而你实际做的事情确保尽可能高效地完成.

在这种情况下,当然加入两个独立的数据源相对较慢,至少与不加入它们相比,因为你需要做的工作是用户请求它的点.

但请记住,替代方案根本不再具有两个单独的数据; 你必须把两个不同的数据点放在同一条记录中.如果没有某种结果,您无法组合两个不同的数据,因此请确保您理解权衡.

好消息是现代关系数据库擅长连接.你不应该认为连接速度慢,好的数据库使用得很好.该数据库提供了许多可扩展性,友好的方式吃生的加入,让他们快:

  • 加入代理键(autonumer/identity column)而不是自然键.这意味着在连接操作期间进行较小(因此更快)的比较
  • 索引
  • 物化/索引视图(将其视为预先计算的连接或管理的非规范化)
  • 计算列.您可以使用它来哈希或以其他方式预先计算连接的键列,这样,对于连接而言,复杂的比较现在要小得多,并且可能预先编入索引.
  • 表分区(通过将负载分散到多个磁盘来帮助处理大型数据集,或者将表扫描可能限制为分区扫描)
  • OLAP(预先计算某些类型的查询/连接的结果.这不完全正确,但您可以将其视为通用非规范化)
  • 复制,可用性组,日志传送或其他机制,让多个服务器应答同一数据库的读取查询,从而在多个服务器之间扩展您的工作负载.

我会说关系数据库存在的主要原因是允许你有效地加入*.它当然不仅仅是存储结构化数据(你可以使用像csv或xml这样的平面文件结构).我列出的一些选项甚至可以让你提前完全建立你的连接,所以在你发出查询之前已经完成了结果 - 就像你已经对数据进行了非规范化一样(当然是以较慢的写操作为代价).

如果您的连接速度较慢,则可能无法正确使用数据库.

只有在这些其他技术失败后才能进行去标准化.而能够真正判断"失败"的唯一方法是设定有意义的绩效目标并衡量这些目标.如果你还没有测量过,现在考虑去标准化还为时过早.

*即,作为不仅仅是表集合的实体存在.真正的rdbms的另一个原因是安全的并发访问.

  • 索引应该位于列表的顶部.许多(*咳嗽*)开发人员在测试小型数据集时似乎忘记了它们,然后在生产中使数据库陷入困境.通过添加索引,我看到的查询运行速度提高了100,000倍.这是任意索引,甚至没有进行任何深入的数据分析来确定最左边前缀匹配的最佳组合. (11认同)

Ten*_*she 30

连接可能比通过去规范化避免它们更慢但如果正确使用(连接具有适当索引的列等等)它们本身并不慢.

如果精心设计的数据库模式存在性能问题,则非规范化是您可以考虑的众多优化技术之一.

  • ...除了在MySQL中,无论索引的外观如何,似乎都存在大量连接的性能问题.或者至少在过去. (2认同)
  • 重点是,如果特定的DBMS(甚至可能是版本)存在已知问题,那么这个建议可能有意义,但作为一般建议,如果您使用关系数据库则会产生误导.这就是说非关系存储机制正变得越来越流行亚马逊的SimpleDB和CouchDB(http://couchdb.apache.org/)就是例子.如果您通过抛弃关系模型来获得更好的服务,那么您应该留下针对后方优化的产品并寻找其他工具. (2认同)

And*_*rey 12

文章说,与缺少连接相比,它们很慢.这可以通过非规范化来实现.所以在速度和标准化之间需要权衡.不要忘记过早优化也:)


小智 12

首先,关系数据库的存在理由(存在的原因)是能够模拟实体之间的关系.联接只是我们遍历这些关系的机制.它们肯定会以一个名义上的成本来实现,但是没有连接,就没有理由拥有关系数据库.

在学术界我们学习各种常规形式(第1,第2,第3,博伊斯 - 科德等),我们学习不同类型的键(主要,外来,替代,独特等)以及如何这些东西结合在一起设计数据库.我们学习SQL的基础知识以及操纵结构和数据(DDL和DML).

在企业界,许多学术结构的可行性远远低于我们所认为的可行性.一个完美的例子是主键的概念.在学术上,它是唯一标识表中一行的属性(或属性集合).因此,在许多问题域中,正确的学术主键是3或4个属性的组合.但是,现代企业界几乎每个人都使用自动生成的顺序整数作为表的主键.为什么?两个原因.首先是因为当你在整个地方迁移FK时,它使模型更加清洁.第二个,与此问题最密切相关的是,通过连接检索数据在单个整数上比在4个varchar列上更快更有效(正如一些人已经提到的那样).

让我们更深入地了解现实世界数据库的两个特定子类型.第一种类型是事务数据库.这是许多驱动现代网站的电子商务或内容管理应用程序的基础.使用事务DB,您可以大幅优化"事务吞吐量".大多数商业或内容应用程序必须平衡查询性能(来自某些表)和插入性能(在其他表中),尽管每个应用程序都有自己独特的业务驱动问题需要解决.

第二种类型的真实世界数据库是报告数据库.它们几乎专门用于聚合业务数据并生成有意义的业务报告.它们的形状通常与生成数据的事务数据库不同,并且它们针对批量数据加载(ETL)的速度和使用大型或复杂数据集的查询性能进行了高度优化.

在每种情况下,开发人员或DBA都需要仔细权衡功能和性能曲线,并且在等式的两边都有许多性能增强技巧.在Oracle中,您可以执行所谓的"解释计划",以便您可以具体了解如何解析和执行查询.您希望最大化数据库正确使用索引.一个非常讨厌的禁忌是将函数放在查询的where子句中.无论何时执行此操作,都可以保证Oracle不会在该特定列上使用任何索引,并且您可能会在解释计划中看到完整或部分表扫描.这只是一个特定的例子,说明如何编写一个最终变慢的查询,并且它与连接没有任何关系.

虽然我们在谈论表扫描,但它们显然会影响查询速度与表的大小成比例.100行的全表扫描甚至不明显.在具有1亿行的表上运行相同的查询,您需要在下周回来进行返回.

我们来谈谈一分钟的规范化.这是另一个很大程度上积极的学术话题,可以过度强调.大多数时候,当我们讨论规范化时,我们的意思是通过将重复数据放入其自己的表并迁移FK来消除重复数据.人们通常会跳过2NF和3NF描述的整个依赖性事物.然而在极端的情况下,拥有一个完美的BCNF数据库肯定是可能的,因为它是如此规范化的,它是一个巨大而且完整的野兽来编写代码.

那么我们在哪里平衡?没有一个最好的答案.所有更好的答案往往是在结构维护的简易性,数据维护的简易性和代码创建/维护的简易性之间的一些妥协.一般来说,数据重复越少越好.

那么为什么连接有时会变慢?有时它的关系设计很糟糕.有时索引无效.有时这是一个数据量问题.有时它是一个可怕的书面查询.

对于这样一个冗长的答案感到抱歉,但我觉得有必要在我的评论中提供一个更加强大的背景,而不是仅仅发出4-bullet响应.


HLG*_*GEM 9

拥有terrabyte大小的数据库的人仍然使用连接,如果他们可以让他们在性能方面工作,那么你也可以.

有很多理由不去反复化.首先,选择查询的速度不是数据库的唯一或甚至主要问题.数据的完整性是首要关注的问题.如果你进行非规范化,则必须采用技术来保持数据在父数据发生变化时非规范化.因此,假设您将客户端名称存储在所有表中,而不是加入client_Id上的客户端表.现在,当客户端的名称发生更改时(客户端的某些名称将随着时间的推移而变化的可能性为100%),现在您需要更新所有子记录以反映该更改.如果你这样做WIL级联更新,你有一百万儿童的记录,怎么快你认为那将是多少用户会遭受锁定问题和延误他们的工作,同时它发生?由于"连接速度慢"而导致非规范化的大多数人对数据库知之甚少,无法正确确保其数据完整性受到保护,并且由于完整性如此糟糕,往往最终会导致数据库具有无法使用的数据.

非规范化是一个复杂的过程,如果要正确完成,需要彻底了解数据库性能和完整性.除非你对员工有这样的专业知识,否则不要试图去反规范化.

如果你做了几件事情,加入的速度非常快.首先使用suggorgate键,int join几乎是alawys最快的连接.第二个总是索引外键.使用派生表或连接条件创建要过滤的较小数据集.如果您拥有庞大而非常复杂的数据库,那么请聘请具有分区和管理大型数据库经验的专业数据库人员.有很多技术可以在不消除连接的情况下提高性能.

如果您只需要查询功能,那么您可以设计一个可以非规范化的数据仓库,并通过ETL工具(针对速度进行优化)填充,而不是用户数据输入.


Pau*_*sik 8

如果加入很慢

  • 数据索引不正确
  • 结果很差
  • 加入查询写得不好
  • 数据集非常庞大和复杂

所以,确实,您的数据设置越大,查询所需的处理越多,但检查和处理上述三个选项通常会产生很好的结果.

您的源提供非规范化作为选项.只要你已经用尽了更好的替代品,这就没问题了.


Qua*_*noi 7

如果需要扫描每侧的大部分记录,则连接可能会很慢.

像这样:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id
Run Code Online (Sandbox Code Playgroud)

即使定义了索引account_customer,仍然需要扫描后者的所有记录.

对于查询列表这个,体面的优化器甚至不会考虑索引访问路径,做一个HASH JOIN或一个MERGE JOIN代替.

请注意,对于这样的查询:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id
WHERE   customer_last_name = 'Stellphlug'
Run Code Online (Sandbox Code Playgroud)

连接很可能会很快:首先,索引customer_last_name将用于过滤所有Stellphlug(当然,不是很多),然后account_customer将为每个Stellphlug发出索引扫描以查找他的交易.

尽管这些可能是数十亿条记录中accountscustomers,只有少数会实际上需要进行扫描.


Mar*_*ams 6

Joins are fast.联接应被视为具有正确规范化数据库模式的标准做法。联接允许您以有意义的方式联接不同的数据组。不要害怕加入。

需要注意的是,您必须了解规范化、连接和索引的正确使用。

当心过早优化,因为所有开发项目的第一个失败就是赶上最后期限。一旦你完成了项目,并且你理解了权衡,如果你能证明它的合理性,你就可以打破规则。

确实,随着数据集大小的增加,连接性能会非线性地降低。因此,它的伸缩性不如单表查询好,但它仍然可以伸缩。

鸟没有翅膀飞得更快,但只能直飞,这也是事实。