单个查询的速度快于联接的速度吗?


44

概念性问题:单个查询的速度是否比联接更快?或者:我是否应该尝试将客户端上想要的每个信息都压缩到一个 SELECT语句中,或者仅使用看起来方便的数量?

TL; DR:如果加入的查询花费的时间比运行单个查询所花费的时间更长,这是我的错还是应该这样?

首先,我不是非常了解数据库,所以可能只是我一个人,但是我注意到当我必须从多个表中获取信息时,通过对单个表进行多次查询来获取此信息“通常”更快(也许包含一个简单的内部联接)并在客户端将数据修补在一起,以尝试编写一个(复杂的)联接查询,在此我可以在一个查询中获得所有数据。

我试图将一个非常简单的示例放在一起:

SQL小提琴

模式设置

CREATE TABLE MASTER 
( ID INT NOT NULL
, NAME VARCHAR2(42 CHAR) NOT NULL
, CONSTRAINT PK_MASTER PRIMARY KEY (ID)
);

CREATE TABLE DATA
( ID INT NOT NULL
, MASTER_ID INT NOT NULL
, VALUE NUMBER
, CONSTRAINT PK_DATA PRIMARY KEY (ID)
, CONSTRAINT FK_DATA_MASTER FOREIGN KEY (MASTER_ID) REFERENCES MASTER (ID)
);

INSERT INTO MASTER values (1, 'One');
INSERT INTO MASTER values (2, 'Two');
INSERT INTO MASTER values (3, 'Three');

CREATE SEQUENCE SEQ_DATA_ID;

INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.5);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 1, 1.7);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 2, 2.3);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.14);
INSERT INTO DATA values (SEQ_DATA_ID.NEXTVAL, 3, 3.7);

查询A

select NAME from MASTER
where ID = 1

结果

| NAME |
--------
|  One |

查询B

select ID, VALUE from DATA
where MASTER_ID = 1

结果

| ID | VALUE |
--------------
|  1 |   1.3 |
|  2 |   1.5 |
|  3 |   1.7 |

查询C

select M.NAME, D.ID, D.VALUE 
from MASTER M INNER JOIN DATA D ON M.ID=D.MASTER_ID
where M.ID = 1

结果

| NAME | ID | VALUE |
---------------------
|  One |  1 |   1.3 |
|  One |  2 |   1.5 |
|  One |  3 |   1.7 |

当然,我没有衡量这些性能,但是可以观察到:

  • 查询A + B返回与查询C相同数量的可用信息。
  • A + B必须向客户端返回1 + 2x3 == 7个“数据单元”
  • C必须将3x3 == 9“数据单元”返回给客户端,因为使用联接,我自然会在结果集中包括一些冗余。

从此概括(按实际情况):

与接收相同信息量的单个查询相比,联接的查询总是必须返回更多的数据。由于数据库必须将数据拼凑在一起,因此对于大型数据集,可以假设数据库在单个联接的查询上必须比在单个查询上做更多的工作,因为(至少)它必须将更多的数据返回给客户端。

会因此得出结论,当我观察到将客户端查询拆分为多个查询会产生更好的性能时,这只是解决之道,还是意味着我搞砸了合并的查询?


评论不作进一步讨论;此对话已转移至聊天
杰克·道格拉斯

1
我运行了一个基准,并将结果发布在Medium上的一篇文章中。我会在这里添加一个答案,但是已经在另一个问题上做到了,而对多个问题发布相同的答案就不那么受欢迎了
本杰明

Answers:


45

单个查询的速度是否比联接的速度快,或者:我应该尝试将客户端上想要的每个信息都压缩到一个SELECT语句中,还是只使用看起来方便的数量?

在任何性能场景中,您都必须测试和衡量解决方案,以查看哪种更快

就是说,与将源行返回到客户端然后再将其连接到客户端相比,从一个经过适当调整的数据库中连接的结果集几乎总是更快,并且扩展性更好。尤其是,如果输入集很大而结果集很小,则在两种策略的上下文中考虑以下查询:将两个表(每个表大小分别为5 GB)连接在一起,结果集为100行。那是一个极端,但是你明白我的意思。

我已经注意到,当我必须从多个表中获取信息时,通过对单个表(可能包含一个简单的内部联接)进行多次查询并在客户端将数据修补在一起来“尝试”通常会“更快”编写一个(复杂的)联合查询,在其中我可以在一个查询中获取所有数据。

很有可能可以改进数据库架构或索引以更好地服务于您向其抛出的查询。

与接收相同信息量的单个查询相比,联接的查询总是必须返回更多的数据。

通常情况并非如此。在大多数情况下,即使输入集很大,结果集也将比输入总和小得多。

根据应用程序的不同,将非常大的查询结果集返回给客户端是立即出现的危险信号:客户端将如何处理如此庞大的数据集,而这些数据无法在更接近数据库的位置进行处理?至少可以说,向用户显示1,000,000行。网络带宽也是一种有限的资源。

由于数据库必须将数据拼凑在一起,因此对于大型数据集,可以假设数据库在单个联接的查询上比在单个查询上要做的工作更多,因为(至少)它必须将更多的数据返回给客户端。

不必要。如果对数据进行了正确的索引,则连接数据库更有可能在数据库中更高效地完成,而无需扫描大量数据。此外,关系数据库引擎在连接的底层进行了特别优化。客户端堆栈不是。

会因此得出结论,当我观察到将客户端查询拆分为多个查询会产生更好的性能时,这只是解决之道,还是意味着我搞砸了合并的查询?

既然您说过您对数据库没有经验,所以我建议您学习有关数据库设计和性能调整的更多信息。我很确定这就是问题所在。编写效率低下的SQL查询也是可能的,但是使用简单的模式不太可能成为问题。

现在,这并不是说没有其他方法可以提高性能。在某些情况下,如果打算使用某种缓存机制,则可以选择扫描中型到大型数据集并将其返回给客户端。缓存可能很棒,但是却在设计中引入了复杂性。缓存甚至可能不适合您的应用程序。

在任何地方都没有提到的一件事是保持从数据库返回的数据的一致性。如果使用单独的查询,则由于多种因素,返回数据的可能性更大(除非为每组查询使用某种形式的快照隔离)。


网络带宽的+1也是有限的资源。
哈里·哈克

OP表示JOINed数据结果集总是更大。>联合查询总是必须返回比单个查询更多的数据。我认为这在客观上是正确的(对于> =),例如,结果集的大小不同,因此在线上的数据更多。你有一个不正确的例子吗?如果我加入Authors-> Posts and Authors有一个名为“ biography”的字段,即1MB JSON字段,对于100个帖子的作者,我将通过有线方式传输100MB与1MB。错了吗
hytromo

6

当然,我没有衡量这些表现

您整理了一些不错的示例代码。您是否看过SQL Fiddle中的计时?甚至一些简短的不科学的性能测试都将表明,演示中的查询三所花费的时间与分别查询一两个人所花的时间差不多。一和二的组合大约是三的两倍,而这是在执行任何客户端联接之前的。

随着数据的增加,查询一和查询二的速度会有所不同,但是数据库联接仍然会更快。

您还应该考虑如果内部联接消除数据会发生什么情况。


2

也应该考虑查询优化器。它的作用是采用声明式SQL并将其转换为过程步骤。为了找到最有效的程序步骤组合,它将检查索引使用,排序,缓存中间结果集以及各种其他内容的组合。即使看起来很简单的查询,排列的数量也可能变得非常大。

为了找到最佳计划而进行的许多计算都是由表中数据的分布决定的。这些分布被采样并存储为统计对象。如果这些是错误的,则它们会导致优化程序做出错误的选择。计划初期的选择不当会导致后来的选择更差,造成雪球效应。

中型查询返回适量的数据要花费几分钟才能运行,这并非鲜为人知。正确的索引编制和良好的统计信息将其减少到毫秒。


-3

多个查询是解决之道。如果您处理类似的简单情况,那么查询优化器的成本开销就是一个因素。数据越多,连接(冗余行)的网络效率就越低。只有大量数据才能提高效率。

最后,您所体验到的是许多开发人员看到的东西。DBA总是说“不,请加入”,但现实是:在这种情况下,进行多个简单选择会更快。


5
联接中没有“网络效率低下”-一切都发生在数据库服务器上,因此不涉及网络(除非您通过数据库链接联接!)
Chris Saxon

2
您可能要考虑网络层是否具有压缩功能。Oracle的SQL * Net可以有效压缩同一列中重复的值。
David Aldridge

3
@TomTom您可能有一个要点(正如David Aldridge所说,压缩很重要),但是您的措辞令人困惑。“加入网络效率低下”?确实,请解决此问题,以使您明白所要表达的意思。
ypercubeᵀᴹ

@ChrisSaxon肯定有,图像中有用于报告“ title-> base-> table-rows”的表,并且需要所有行,因此您可以内部连接这3个表。每个表都有长的varchars,因此对于重复这些长的varchars的每一行会发生什么。应用程序层需要为所有这些字符串分配内存,然后为模型分组它们。所以我认为这就是他的意思,发送了更多数据
MIKE

@MIKE取决于您选择的表达式,而不取决于联接。并且可能存在网络压缩。在Oracle数据库SQL * Net的移除了反复重复的值nicetheory.io/2018/01/11/...
克里斯-撒克逊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.