为什么联接不好或“慢”。我知道我再听一次。我找到了这句话
问题是联接相对较慢,尤其是在非常大的数据集上,如果联接较慢,则您的网站也很慢。从磁盘上获取所有这些单独的信息位并将它们重新组合在一起需要很长时间。
我一直以为他们很快,尤其是在查找PK时。他们为什么“慢”?
为什么联接不好或“慢”。我知道我再听一次。我找到了这句话
问题是联接相对较慢,尤其是在非常大的数据集上,如果联接较慢,则您的网站也很慢。从磁盘上获取所有这些单独的信息位并将它们重新组合在一起需要很长时间。
我一直以为他们很快,尤其是在查找PK时。他们为什么“慢”?
Answers:
可伸缩性是关于预先计算,分散或将重复的工作缩减为基本要素的全部目的,以最大程度地减少每个工作单元的资源使用。为了实现良好的伸缩性,您无需做任何不需要做的事情,而您实际上要确保所做的事情能尽可能高效地完成。
在这种情况下,连接两个单独的数据源当然相对较慢,至少与不将它们连接起来相比,这是因为您需要在用户要求的时候进行工作。
但是请记住,替代方案是根本不再拥有两个单独的数据。您必须将两个不同的数据点放在同一记录中。您不能在没有任何结果的情况下将两个不同的数据合并在一起,因此请确保您了解了取舍。
好消息是现代关系数据库擅长联接。您不应该真的认为使用良好的数据库会导致连接缓慢。有许多的可扩展性,友好的方式吃生的加入,让他们多快:
我会说关系数据库根本存在的主要原因是允许您高效地进行联接*。当然,不仅仅是存储结构化数据(您可以使用csv或xml等平面文件结构来实现此目的)。我列出的一些选项甚至可以让您预先完全建立连接,因此在发出查询之前结果已经完成-就像您对数据进行了非规范化一样(以降低写操作速度为代价)。
如果加入速度较慢,则可能是您未正确使用数据库。
只有在这些其他技术都失败之后,才能进行非规范化。真正判断“失败”的唯一方法是设定有意义的性能目标并根据这些目标进行衡量。如果您还没有衡量的话,甚至考虑去规范化还为时过早。
*即,作为实体存在于表的单独集合之外。真正的rdbms的另一个原因是安全的并发访问。
连接可能比通过反规范化避免连接要慢,但是如果正确使用(在具有适当索引的列上连接等等),它们并不是天生就不会慢。
如果设计良好的数据库架构出现性能问题,则可以考虑使用非规范化许多优化技术之一。
首先,关系数据库的存在理由(存在的理由)将能够为实体之间的关系建模。联接只是我们遍历这些关系的机制。当然,它们确实需要象征性的代价,但是如果没有联接,则实际上没有理由拥有关系数据库。
在学术界,我们学习诸如各种正常形式(第一,第二,第三,博伊斯-科德等)之类的东西,并且了解不同类型的键(主键,外键,交替键,唯一键等)以及如何使用这些东西适合设计数据库。而且,我们学习了SQL的基本知识以及结构和数据(DDL和DML)。
在企业界,许多学术构想的可行性远不如我们想象的那样。一个完美的例子是主键的概念。从学术上讲,属性(或属性集合)唯一地标识了表中的一行。因此,在许多问题领域中,正确的学术主键是3或4个属性的组合。但是,现代公司世界中的几乎每个人都使用自动生成的顺序整数作为表的主键。为什么?有两个原因。首先是因为当您到处迁移FK时,它使模型更整洁。第二个问题,也是与这个问题最密切相关的一个问题,是在单个整数上通过联接检索数据比在4个varchar列上检索数据更快,更高效(正如一些人已经提到的那样)。
现在让我们更深入地研究现实数据库的两个特定子类型。第一种类型是事务数据库。这是驱动现代网站的许多电子商务或内容管理应用程序的基础。使用事务数据库,您正在朝“事务吞吐量”进行大量优化。大多数商务或内容应用程序必须在查询性能(来自某些表)和插入性能(在其他表中)之间取得平衡,尽管每个应用程序都有其自己独特的业务驱动问题来解决。
现实世界数据库的第二种类型是报告数据库。这些几乎专门用于汇总业务数据并生成有意义的业务报告。它们的形状通常与生成数据的事务数据库的形状不同,并且它们针对大量或复杂数据集的批量数据加载(ETL)的速度和查询性能进行了高度优化。
在每种情况下,开发人员或DBA都需要仔细平衡功能和性能曲线,并且在等式两边都有很多性能增强的技巧。在Oracle中,您可以执行所谓的“解释计划”,从而可以具体查看查询的解析和执行方式。您正在寻求最大化数据库对索引的正确使用。一个非常讨厌的禁忌是将一个函数放在查询的where子句中。每当您这样做时,都可以保证Oracle不会在该特定列上使用任何索引,并且您可能会在说明计划中看到完整或部分表扫描。那只是一个如何编写查询的特定示例,该查询最终变得很慢,并且与联接无关。
而且,当我们谈论表扫描时,它们显然会与表的大小成比例地影响查询速度。对100行的全表扫描甚至没有引起注意。在具有1亿行的表上运行相同的查询,您需要在下周返回以获得收益。
让我们讨论一下规范化。这是另一个过于积极的学术话题,可能会过分强调。在大多数情况下,当我们谈论规范化时,实际上是指通过将重复数据放入自己的表中并迁移FK来消除重复数据。人们通常会跳过2NF和3NF描述的整个依赖项。但是,在极端情况下,肯定有可能拥有一个庞大而完善的BCNF数据库,并且因为它是如此规范化,因此可以编写代码的完整野兽。
那么我们在哪里平衡呢?没有一个最佳答案。所有更好的答案往往是在结构维护的便利性,数据维护的便利性和代码创建/维护的便利性之间做出了一些折衷。通常,重复数据越少越好。
那么,为什么有时加入速度慢?有时这是糟糕的关系设计。有时这是无效的索引。有时是数据量问题。有时这是一个可怕的书面查询。
抱歉,这么冗长的回答,但是我感到不得不为我的评论提供一个更加具体的背景,而不是仅仅听从四子弹的回应。
具有terrabyte大小的数据库的人仍在使用联接,如果他们可以使它们按性能运行,那么您也可以。
有很多原因不进行降级处理。首先,选择查询的速度并不是数据库的唯一或什至主要关注的问题。数据的完整性是首要考虑的问题。如果您进行非规范化,则必须采用适当的技术来使数据在父数据更改时保持非规范化。因此,假设您要在所有表中存储客户端名称,而不是在client_Id上加入客户端表。现在,当客户端名称更改时(某些客户端名称随时间变化的可能性为100%),现在您需要更新所有子记录以反映该更改。如果您进行级联更新并拥有一百万条子记录,那么您认为这种记录会以多快的速度发生?有多少用户在发生这种情况时会遇到锁定问题和工作延迟?此外,由于“
非规范化是一个复杂的过程,如果要正确完成,则需要彻底了解数据库的性能和完整性。除非您对员工有专门知识,否则请不要尝试使其规范化。
如果您做几件事,联接就足够快了。首先使用Suggorgate键,一个int联接几乎是最快的联接。第二,总是索引外键。使用派生表或联接条件创建一个较小的数据集进行过滤。如果您有一个非常复杂的大型数据库,请雇用一个具有丰富的分区和管理经验的专业数据库人员。有很多技术可以提高性能而又不脱离联接。
如果您只需要查询功能,那么可以,您可以设计一个数据仓库,该仓库可以进行规范化,并通过ETL工具(针对速度进行了优化)而不是用户数据输入来填充。
如果需要扫描来自每一侧的大部分记录,则连接可能很慢。
像这样:
SELECT SUM(transaction)
FROM customers
JOIN accounts
ON account_customer = customer_id
即使在上定义了索引account_customer
,仍然需要扫描来自后者的所有记录。
对于这个查询列表,体面的优化器可能甚至不会考虑索引访问路径,而是使用a HASH JOIN
或a MERGE JOIN
。
请注意,对于这样的查询:
SELECT SUM(transaction)
FROM customers
JOIN accounts
ON account_customer = customer_id
WHERE customer_last_name = 'Stellphlug'
连接很可能会很快:首先,customer_last_name
将使用索引on 过滤所有Stellphlug(当然不是很多),然后account_customer
将为每个Stellphlug发出索引扫描以查找他的交易。
尽管这些可能是数十亿条记录中accounts
和customers
,只有少数实际上需要进行扫描。
accounts(account_customer)
大多数RDBMS 上定义了索引,则该索引将使用该索引来准确找出customers
需要扫描数据库的哪些行。
HASH JOIN
会快得多,因此将在所有主要数据库中使用,除了MySQL
,它将除外,这只会customers
导致嵌套循环(因为它的大小更小)
Joins are fast.
联接应被视为具有适当规范化数据库架构的标准实践。联接使您能够以有意义的方式联接不同的数据组。不要害怕加入。
需要注意的是,您必须了解索引的规范化,连接和正确使用。
提防过早的优化,因为所有开发项目中的第一大失败就是在最后期限之前完成。一旦完成了项目,并且了解了折衷方案,只要可以证明其合理性,就可以违反规则。
的确,随着数据集大小的增加,联接性能会非线性下降。因此,它不能像单表查询那样很好地扩展,但是仍然可以扩展。
确实,一只鸟没有任何翅膀就可以飞得更快,但只能直下。
联接确实需要额外的处理,因为它们必须查找更多文件和更多索引以将数据“联接”在一起。但是,“非常大的数据集”都是相对的。large的解释是什么?就JOIN而言,我认为它是对大型结果集的引用,而不是对整个数据集的引用。
大多数数据库可以非常快速地处理查询,该查询从主表中选择5条记录,并为每条记录从相关表中联接5条记录(假设已建立正确的索引)。这些表每个可以包含数亿条记录,甚至数十亿条。
一旦结果集开始增长,事情就会变慢。使用同一示例,如果主表产生100K条记录,则将需要找到500K条“联接”记录。只是将大量数据从数据库中拉出会增加延迟。
不要避开JOIN,只是要知道当数据集变得“非常大”时可能需要优化/去规范化。
同样从您引用的文章中:
每天都有数十亿个记录,PB级数据,数千个并发用户以及数百万个查询的许多大型网站正在使用分片方案,甚至有人主张将非规范化作为构建数据层的最佳策略。
和
而且,除非您是一个非常大的网站,否则您可能不必担心这种复杂性。
和
比让数据库完成所有这些工作更容易出错,但是您可以扩展甚至超过最高端数据库可以处理的范围。
本文正在讨论像Ebay这样的大型网站。在这种使用水平下,您可能不得不考虑普通的关系数据库管理之外的其他内容。但是在“正常”的业务过程(具有数千个用户和数百万条记录的应用程序)中,那些更昂贵,更容易出错的方法是过大的。
基于联接,生成的临时数据量可能很大。
例如,这里工作的一个数据库具有通用搜索功能,其中所有字段都是可选的。搜索例程在搜索开始之前对每个表进行了联接。在一开始就很好。但是,现在主表已经超过一千万行了……还不行。现在,搜索需要30分钟或更长时间。
我的任务是优化搜索存储过程。
我做的第一件事是,如果要搜索主表中的任何字段,我只对那些字段上的临时表进行选择。然后,在进行其余搜索之前,我将所有表与该临时表连接在一起。现在搜索主表字段之一所需的时间少于10秒。
如果没有开始搜索主表字段,则对其他表进行类似的优化。完成后,搜索时间不会超过30秒,而大多数搜索时间都在10秒以下。
SQL Server的CPU使用率也下降了。
尽管联接(可能归因于规范化的设计)对于数据检索而言显然比从单个表中读取要慢,但由于规范化数据库的总事务量不会最小,因此非规范化的数据库对于数据创建/更新操作可能较慢。
在规范化的数据库中,一条数据将仅存放在一个地方,因此更新的占用空间将尽可能少。在非规范化数据库中,可能必须更新多行或跨表的同一列,这意味着占用空间会更大,并且出现锁和死锁的机会会增加。
如果草率地做,它们可能会变慢。例如,如果对联接执行“选择*”,则可能需要一段时间才能将东西取回。但是,如果您仔细选择要从每个表中返回哪些列,并且具有适当的索引,则应该没有问题。