JOIN查询与多个查询

Tho*_*ini 160 mysql database join query-optimization

JOIN查询比几个查询更快吗?(您运行主查询,然后根据主查询的结果运行许多其他SELECT)

我问,因为加入它们会使我的应用程序的设计变得复杂

如果它们更快,那么任何人都可以近似粗略估计多少?如果它是1.5倍我不在乎,但如果它是10倍我想我做.

小智 88

对于内部联接,单个查询是有意义的,因为您只获得匹配的行.对于左连接,多个查询要好得多......看看我做的以下基准测试:

  1. 单个查询,包含5个联接

    查询:8.074508秒

    结果大小:2268000

  2. 连续5次查询

    合并查询时间:0.00262秒

    结果大小:165(6 + 50 + 7 + 12 + 90)

.

请注意,我们在两种情况下都得到相同的结果(6 x 50 x 7 x 12 x 90 = 2268000)

left join使用冗余数据指数级地增加内存.

如果您只进行两个表的连接,则内存限制可能不会那么糟糕,但通常是三个或更多,并且它变得值得进行不同的查询.

作为旁注,我的MySQL服务器就在我的应用服务器旁边......所以连接时间可以忽略不计.如果你的连接时间是几秒钟,那么也许有一个好处

坦率

  • 如果我们抛开一个令人讨厌的小事实,即在他们正确的思想中没有人在5个表之间进行交叉连接(因为这个原因,在大多数情况下,它只是没有意义*),你的"基准"可能有一些优点.但是左边或内部连接是常态,通常是按键(使检索更快),并且数据的重复通常比你实现的要多得多*.*. (29认同)
  • @cHao说谁?我只是查看了SMF和phpBB,看到了3个表之间的JOIN - 如果你添加插件或修改,他们可以很容易地添加到那里.任何类型的大型应用程序都有可能存在许多JOIN.可以说,写得不好/使用不当的ORM可能会加入它实际上并不需要的表(甚至可能是每个表). (10认同)
  • @NathanAdams:左边和内部连接都不差.(事实上​​,如果你不是在这里和那里连接表,那你就错误地做了SQL.)我所说的是*交叉连接*,即使在两个表之间也几乎总是不受欢迎的,更不用说5 - 和这将是获得上面提到的完全伪造的"2268000"结果的唯一方法. (5认同)
  • @cHao显然,您在发表第一条评论时尚未见过Magento (3认同)
  • 但是看看结果."结果大小:2268000"与"结果大小:165".我认为你对JOIN的减速是因为你的记录彼此之间有一对多的关系,而如果他们有一对一的关系,那么JOIN肯定会快得多,而且肯定不会有结果尺寸大于SELECT. (2认同)
  • @vitoriodachef:实际上还没有。但我坚持这个评论。交叉联接意味着每一个可能的行配对都是有意义的——这在“两个”表之间是很少见的,我从来不想这样做。有一种编写查询的方法,使它们“看起来”像交叉联接,而实际上它们是内部联接,但只要您有唯一索引或主键,数据库就会更好地了解。(如果 Magento 存活了这么久,那么它的数据库模式就是这样,您“永远”没有理由在五个表之间进行交叉连接。) (2认同)

Pao*_*ino 71

这太模糊了,无法给出与您的具体案例相关的答案.这取决于很多事情.Jeff Atwood(这个网站的创始人)实际上写过这个.但是,在大多数情况下,如果你有正确的索引,并且你正确地进行了JOIN,那么一次旅行通常比几次更快.

  • 我认为这取决于你对"更快"的定义...例如,由于网络开销,3个PK内连接可能比4个往返更快转,并且因为你需要停止并准备并发送每个查询后上一个查询完成.但是,如果要对负载下的服务器进行基准测试,则在大多数情况下,连接将比PK查询花费更多的CPU时间,并且通常也会导致更多的网络开销. (4认同)
  • 如果你在不同的键上加入3个或更多的表,通常数据库(即mysql)每个表只能使用一个索引,这意味着其中一个连接将是快速的(并使用索引),而其他连接将非常慢.对于多个查询,您可以优化要用于每个查询的索引. (2认同)

Val*_*sel 21

实际上,我自己也在寻找答案,在阅读了给定的答案之后,我只能同意比较数据库查询性能的最佳方法是获取真实世界的数字,因为有很多变量需要考虑但是,我也认为比较它们之间的数字几乎在所有情况下都没有好处.我的意思是,数字应始终与可接受的数字进行比较,绝对不能相互比较.

我可以理解,如果一种查询方式需要0.02秒而另一种需要20秒,这是一个巨大的差异.但是,如果一种查询方式需要0.0000000002秒,而另一种方法需要0.0000002秒呢?在这两种情况下,一种方式比另一种方式快1000倍,但在第二种情况下它真的仍然"惊人"吗?

我亲眼看到它的底线:如果它表现良好,那就选择简单的解决方案.

  • 当然,这取决于你是否计划扩展.当Facebook开始时我很确定他们有这样的疑问,但是考虑到了扩展并且考虑了更有效但可能更复杂的解决方案. (4认同)
  • 哈哈,是的...因为在谷歌1纳秒的损失实际上相当于100亿亿美元......但这只是一个谣言. (4认同)
  • @dudewad 实际上,当 Facebook 开始时,我保证他们采用了更简单的解决方案。扎克伯格说他只用了两周就编写了第一个版本。初创企业需要_快速_进行竞争,而幸存下来的企业通常不会担心扩展,直到他们真正需要它。_然后_他们在拥有数百万美元的投资后重构东西,并且可以聘请专注于性能的摇滚明星程序员。就您而言,我希望 Facebook 现在经常采用更复杂的解决方案来获得微小的性能提升,但那时我们大多数人都没有对 Facebook 进行编程。 (4认同)

小智 13

快速测试是从50,000行表中选择一行并从100,000行表中选择一行.基本上看起来像:

$id = mt_rand(1, 50000);
$row = $db->fetchOne("SELECT * FROM table1 WHERE id = " . $id);
$row = $db->fetchOne("SELECT * FROM table2 WHERE other_id = " . $row['other_id']);
Run Code Online (Sandbox Code Playgroud)

VS

$id = mt_rand(1, 50000);
$db->fetchOne("SELECT table1.*, table2.*
    FROM table1
    LEFT JOIN table1.other_id = table2.other_id
    WHERE table1.id = " . $id);
Run Code Online (Sandbox Code Playgroud)

对于50,000次读取,两种选择方法花费3.7秒,而在家用慢速计算机上,JOIN花费2.0秒.INNER JOIN和LEFT JOIN并没有什么区别.获取多行(例如,使用IN SET)产生类似的结果.


Dre*_*awn 8

构建单独的查询和连接,然后为每个查询和连接计时 - 没有什么比现实世界的数字更有帮助.

然后更好 - 在每个查询的开头添加"EXPLAIN".这将告诉您MySQL使用多少个子查询来回答您的数据请求,以及为每个查询扫描的行数.


Hol*_*ger 8

真正的问题是:这些记录是一对一关系还是一对多关系

TLDR答案:

如果一对一,请使用JOIN声明.

如果是一对多,则使用一个(或多个)SELECT语句与服务器端代码优化.

为什么以及如何使用SELECT进行优化

SELECT基于一对多关系在大型记录组上进行(使用多个查询而不是连接)可以产生最佳效率,因为JOIN存在指数性内存泄漏问题.获取所有数据,然后使用服务器端脚本语言对其进行排序:

SELECT * FROM Address WHERE Personid IN(1,2,3);
Run Code Online (Sandbox Code Playgroud)

结果:

Address.id : 1            // First person and their address
Address.Personid : 1
Address.City : "Boston"

Address.id : 2            // First person's second address
Address.Personid : 1
Address.City : "New York"

Address.id : 3            // Second person's address
Address.Personid : 2
Address.City : "Barcelona"
Run Code Online (Sandbox Code Playgroud)

在这里,我将在一个选择语句中获取所有记录.这比JOIN将要获得一小组这些记录(一次一个)作为另一个查询的子组件要好.然后我用服务器端代码解析它看起来像......

<?php
    foreach($addresses as $address) {
         $persons[$address['Personid']]->Address[] = $address;
    }
?>
Run Code Online (Sandbox Code Playgroud)

何时不使用JOIN进行优化

JOIN基于与一个记录的一对一关系的大量记录产生与多个SELECT语句相比的最佳效率,一个接一个地获得下一个记录类型.

但是JOIN在获得具有一对多关系的记录时效率很低.

示例:数据库博客有3个感兴趣的表,Blogpost,Tag和Comment.

SELECT * from BlogPost
LEFT JOIN Tag ON Tag.BlogPostid = BlogPost.id
LEFT JOIN Comment ON Comment.BlogPostid = BlogPost.id;
Run Code Online (Sandbox Code Playgroud)

如果有1个博文,2个标签和2个评论,您将得到如下结果:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag2, comment1,
Row4: tag2, comment2,
Run Code Online (Sandbox Code Playgroud)

注意每条记录是如何重复的.好的,2条评论和2个标签是4行.如果我们有4条评论和4条标签怎么办?你没有得到8行 - 你得到16行:

Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag1, comment3,
Row4: tag1, comment4,
Row5: tag2, comment1,
Row6: tag2, comment2,
Row7: tag2, comment3,
Row8: tag2, comment4,
Row9: tag3, comment1,
Row10: tag3, comment2,
Row11: tag3, comment3,
Row12: tag3, comment4,
Row13: tag4, comment1,
Row14: tag4, comment2,
Row15: tag4, comment3,
Row16: tag4, comment4,
Run Code Online (Sandbox Code Playgroud)

添加更多表,更多记录等,问题将迅速膨胀到数百行,这些行都充满了大部分冗余数据.

这些重复的费用是什么?内存(在SQL服务器和尝试删除重复项的代码中)和网络资源(在SQL服务器和代码服务器之间).

资料来源:https://dev.mysql.com/doc/refman/8.0/en/nested-join-optimization.html ; https://dev.mysql.com/doc/workbench/en/wb-relationship-tools.html

  • 还有一件事值得指出:更多内存=更慢的性能。与缓存数据上的处理器周期相比,内存速度非常慢。任何使应用程序必须消耗更多内存的因素也会使其实际上处理速度变慢。 (2认同)

Ben*_*min 8

这个问题很旧,但是缺少一些基准。我将JOIN与其2个竞争对手进行了基准测试:

  • N + 1个查询
  • 2个查询,第二个查询使用a WHERE IN(...)或同等查询

其结果是显而易见的:在MySQL,JOIN快。N + 1个查询会大大降低应用程序的性能:

参加vs参加vs N + 1

也就是说,除非您选择很多记录,这些记录都指向极少数不同的外来记录。这是极端情况的基准:

JOIN vs N + 1-所有记录都指向相同的外部记录

除非您要加入多对多关系(在这种情况下,外键在另一个表上,并且您要多次复制主表数据),否则在典型的应用程序中几乎不可能发生这种情况。

带走:

  • 对于一对一关系,请始终使用 JOIN
  • 对于多对多关系,第二个查询可能更快

有关更多信息,请参见我在Medium上的文章


gla*_*snt 7

与开发人员复杂性相比,根据数据库的复杂性,执行许多SELECT调用可能更简单.

尝试针对JOIN和多个SELECTS运行一些数据库统计信息.查看在您的环境中JOIN是否比SELECT更快/更慢.

然后,如果将其更改为JOIN将意味着额外的一天/周/月的开发工作,我会坚持使用多个SELECT

干杯,

BLT


Ram*_*mon 6

吞吐量会更快吗?大概。但它也可能一次锁定更多数据库对象(取决于您的数据库和架构),从而降低并发性。根据我的经验,人们经常被“更少的数据库往返次数”这一论点所误导,而实际上,在数据库位于同一 LAN 上的大多数 OLTP 系统中,真正的瓶颈很少是网络。


小智 5

根据我的经验,我发现运行多个查询通常会更快,特别是在检索大型数据集时.

当从另一个应用程序(例如PHP)与数据库进行交互时,存在一个到服务器的一次访问的争论.

还有其他方法可以限制对服务器的访问次数,并且仍然运行多个查询,这些查询通常不仅更快,而且还使应用程序更易于阅读 - 例如mysqli_multi_query.

在SQL方面,我不是新手,我认为开发人员有一种趋势,特别是初级人员花费大量时间尝试编写非常聪明的连接,因为它们看起来很聪明,而实际上有很多智能方法可以提取看起来很简单的数据简单.

最后一段是个人意见,但我希望这会有所帮助.我确实同意其他人,但是谁说你应该做基准测试.这两种方法都不是银弹.


cHa*_*Hao 5

是否应该使用联接首先要看联接是否有意义。只有在这一点上才需要考虑性能,因为几乎所有其他情况都会导致性能显着下降

性能差异很大程度上取决于您正在查询的信息的相关程度。连接有效,当数据相关并且您正确索引内容时,连接速度很快,但它们通常会导致一些冗余,有时会导致超出所需的结果。如果您的数据集不直接相关,那么将它们放在单个查询中将导致所谓的笛卡尔积(基本上,所有可能的行组合),这几乎不是您想要的。

这通常是由多对一多关系引起的。例如,HoldOffHunger 的答案提到了对帖子、标签和评论的单个查询。评论与帖子相关,标签也是如此……但标签与评论无关。

+------------+     +---------+     +---------+
|  comment   |     |   post  |     |  tag    |
|------------|*   1|---------|1   *|---------|
| post_id    |-----| post_id |-----| post_id |
| comment_id |     | ...     |     | tag_id  |
| user_id    |     |         |     | ...     |
| ...        |     |         |     | ...     |
+------------+     +---------+     +---------+
Run Code Online (Sandbox Code Playgroud)

在这种情况下,至少有两个单独的查询无疑更好。如果您尝试连接标签和评论,因为两者之间没有直接关系,您最终会得到标签和评论的每种可能的组合。many * many == manymany。除此之外,由于评论和标签不相关,因此您可以并行执行这两个查询,从而获得潜在的收益。

不过,让我们考虑一个不同的场景:您希望将评论附加到帖子中,以及评论者的联系信息。

 +----------+     +------------+     +---------+
 |   user   |     |  comment   |     |   post  |
 |----------|1   *|------------|*   1|---------|
 | user_id  |-----| post_id    |-----| post_id |
 | username |     | user_id    |     | ...     |
 | ...      |     | ...        |     +---------+
 +----------+     +------------+
Run Code Online (Sandbox Code Playgroud)

这是您应该考虑加入的地方。除了更加自然的查询之外,大多数数据库系统(包括 MySQL)都有很多聪明的人投入大量的努力来优化查询,就像它一样。对于单独的查询,由于每个查询都依赖于前一个查询的结果,因此查询不能并行完成,总时间不仅是查询的实际执行时间,而且还包括获取结果、筛选结果所花费的时间通过它们获取下一个查询的 ID、将行链接在一起等。