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

时间:2010-04-12 17:02:43

标签: sql join

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

  

问题是连接是相对的   慢,尤其是非常大的数据   套,如果它们很慢你的   网站很慢。这需要很长时间   获得所有这些单独的位   磁盘上的信息并将它们全部放入   再一次。

source

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

16 个答案:

答案 0 :(得分:89)

可扩展性的全部内容是预先计算,分散或减少重复工作到最基本的工作,以最大限度地减少每个工作单元的资源使用。为了更好地扩展,你不需要做任何你不需要的东西,并且你确实做的事情尽可能有效地完成。

在这种情况下,当然加入两个独立的数据源相对较慢,至少与不加入它们相比,因为您需要在用户请求它的时候进行实际工作。

但请记住,替代品不再具有两个独立的数据;你必须把两个不同的数据点放在同一条记录中。你不能在某个地方组合两个不同的数据,所以要确保你理解权衡。

好消息是现代关系数据库在连接时。你不应该认为连接速度慢,好的数据库使用得很好。该数据库提供了许多可伸缩性的方法来获取原始连接并使它们更多更快:

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

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

如果加入速度较慢,则可能无法正确使用数据库。

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

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

答案 1 :(得分:30)

联接可以,而不是通过反规范化来避免它们,但如果使用正确(连接具有适当索引的列,依此类推)它们本身并不慢。< / p>

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

答案 2 :(得分:12)

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

答案 3 :(得分:11)

首先,关系数据库的存在理由(存在的原因)是能够模拟实体之间的关系。联接只是我们遍历这些关系的机制。它们肯定会以象征性的成本出现,但如果没有连接,就没有理由建立关系数据库。

在学术界,我们学习各种常规形式(第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响应。

答案 4 :(得分:9)

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

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

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

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

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

答案 5 :(得分:8)

如果

,联接很慢
  • 数据索引不正确
  • 结果不佳过滤
  • 加入查询写得不好
  • 数据集非常大且复杂

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

您的来源会将非规范化作为一种​​选择。只要你已经用尽了更好的替代品,这很好。

答案 6 :(得分:7)

如果需要扫描每一侧的大部分记录,联接可能会很慢。

像这样:

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id

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

对于查询列表,优秀的优化器甚至不会考虑索引访问路径,而是执行HASH JOINMERGE JOIN

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

SELECT  SUM(transaction)
FROM    customers
JOIN    accounts
ON      account_customer = customer_id
WHERE   customer_last_name = 'Stellphlug'

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

尽管这些记录在accountscustomers中可能是数十亿条记录,但实际上只需要扫描几条记录。

答案 7 :(得分:3)

联接确实需要额外的处理,因为他们必须查看更多文件和更多索引才能将数据“连接”在一起。但是,“非常大的数据集”都是相对的。大的定义是什么?我是JOIN的情况,我认为它是对大型结果集的引用,而不是整个数据集。

大多数数据库可以非常快速地处理从主表中选择5条记录的查询,并为每条记录连接相关表中的5条记录(假设正确的索引已到位)。这些表每个都有数亿条记录,甚至数十亿条记录。

一旦你的结果集开始增长,事情就会变慢。使用相同的示例,如果主表导致100K记录,则将需要找到500K“已加入”记录。只需从数据库中提取大量数据,就会增加延迟。

不要避免使用JOIN,只要知道当数据集“非常大”时你可能需要优化/非规范化。

答案 8 :(得分:3)

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

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

注意过早优化,因为所有开发项目失败的第一个都在满足截止日期。一旦你完成了项目,并且你理解了权衡,你就可以违反规则,如果你能证明这一点。

确实,随着数据集大小的增加,连接性能会非线性降低。因此,它不像单个表查询那样精确地扩展,但它仍然可以扩展。

一只鸟在没有任何翅膀的情况下飞得更快,但只是直接向下飞行也是如此。

答案 9 :(得分:3)

同样来自你引用的文章:

  

许多数十亿的大型网站   记录,PB级数据,很多   成千上万的并发用户,以及   每天有数百万条查询正在进行   使用分片方案,有些是   甚至提倡非正规化作为   建筑的最佳策略   数据层。

  

除非你真的很大   您可能不需要的网站   担心这种复杂程度。

  

它比错误更容易出错   数据库做所有这些工作,但你是   能够做到甚至超过规模   最高端数据库可以处理。

这篇文章正在讨论像Ebay这样的大型网站。在这种使用级别,您可能不得不考虑除了普通的关系数据库管理之外的其他东西。但是在“正常”的业务过程中(拥有数千个用户和数百万条记录的应用程序),那些更昂贵,更容易出错的方法是过度的。

答案 10 :(得分:2)

连接被认为是可扩展性的反对力量,因为它们通常是瓶颈,并且不能轻易分发或并行。

答案 11 :(得分:2)

正确设计的表格包含正确的标记和正确编写的查询并不总是很慢。你曾经在哪里听说过:

  

为什么加入不好或'慢'

不知道他们在说什么!大多数联接会非常快。如果你必须同时加入许多行,那么与非规范化表相比,你可能会受到一次打击,但这可以追溯到正确设计的表,知道何时进行非规范化,何时不进行。在繁重的报告系统中,将非规范化表中的数据分解为报告,甚至创建数据仓库。在事务性繁重的系统中规范化表。

答案 12 :(得分:1)

根据连接,生成的临时数据量可能很大。

例如,这里的一个数据库具有通用搜索功能,其中所有字段都是可选的。在搜索开始之前,搜索例程在每个表上都进行了连接。这在一开始就运作良好。但是,现在主表有超过1000万行......不是那么多。现在搜索需要30分钟或更长时间。

我的任务是优化搜索存储过程。

我做的第一件事是如果搜索主表的任何字段,我只选择这些字段上的临时表。然后,在进行剩余的搜索之前,我使用该临时表加入了所有表。搜索其中一个主表字段现在只需不到10秒的时间。

如果没有开始搜索主表字段,我会对其他表执行类似的优化。当我完成时,搜索时间不超过30秒,大多数都不到10秒。

SQL服务器的CPU利用率也变为WAY DOWN。

答案 13 :(得分:1)

虽然连接(可能是由于规范化设计)对于数据检索而言显然比从单个表中读取更慢,但是非规范化数据库对于数据创建/更新操作来说可能很慢,因为整个事务的占用空间不会是最小的。

在规范化数据库中,一段数据将仅存在于一个位置,因此更新的占用空间将尽可能小。在非规范化数据库中,可能必须更新多行或跨表中的相同列,这意味着占用空间会更大,锁定和死锁的可能性会增加。

答案 14 :(得分:1)

嗯,是的,从一个非规范化表中选择行(假设您的查询具有合适的索引)可能比选择通过连接多个表构造的行更快,特别是如果连接没有可用的有效索引。

文章中引用的例子--Flickr和eBay--是IMO的例外情况,因此具有(并且值得)特殊的回应。作者特别提到了文章中缺少RI和数据重复的程度。

大多数应用 - 再次,IMO - 受益于验证&amp;减少了RDBMS提供的重复。

答案 15 :(得分:0)

如果做得不好,它们可能会很慢。例如,如果你在一个连接上做'select *',你将需要花费一些时间才能恢复。但是,如果您仔细选择从每个表返回的列,并且使用适当的索引,则应该没有问题。