一个大查询或多个小查询哪个更快?


68

我曾在不同的公司工作过,并且我注意到其中有些人更喜欢将视图与所有“亲戚”一起加入表格中。但是随后在应用程序上,我们只需要使用1列。

因此,仅进行简单选择,然后在系统代码上“联接”它们会更快吗?

该系统可以是php,java,asp,也可以是连接到数据库的任何语言。

所以问题是,从服务器端(php,java,asp,ruby,python ...)到数据库的运行速度更快,运行一个查询即可获得我们所需的一切,或者从服务器端运行到数据库并运行一个查询一次只能从一个表中获取列?


2
您正在使用哪种“ SQL”实现?MySQL,Microsoft SQL Server,Oracle,Postgresql等?请更新您的标签。
RLF 2014年

1
Mysql和Postgresql
sudo.ie 2014年

6
我的经验是,MySQL不喜欢复杂的查询,通常使用非常简单的查询(甚至更多)会更快。Postgres的查询优化器要好得多,运行单个大型查询通常效率更高。
a_horse_with_no_name 2014年

3
@a_horse_with_no_name这是非常广泛的概括,尤其是在此问题的上下文中。MySQL优化器的设计确实确实非常简单,并且可能导致联接和子查询(特别是在旧版本的MySQL上)出现问题,否则它们会在PostgreSQL中产生更快的计划,而MySQL对于纯OLTP加载可能非常快。但是,在问题的上下文中,单个大查询的速度会更快,例如,在更糟糕的情况下,在编程循环内进行SELECT(无论使用的是RDBMS)。
jynus 2014年

2
@jynus:嗯,这个问题非常广泛的(加:我说:“在我的经验” -其他人可能有不同的经验)。在LOOP中进行查询从来都不是一个好主意,并且几乎总是由于设计不良或缺乏对如何使用关系数据库的理解而导致的。
a_horse_with_no_name 2014年

Answers:


68

可以解决您的问题的主题是JOIN DECOMPOSITION。

根据《书》第209页

高性能MySQL

您可以通过运行多个单表查询而不是多表联接来分解联接,然后在应用程序中执行联接。例如,代替以下单个查询:

SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql';

您可以运行以下查询:

SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

你到底为什么要这样做?乍一看似乎很浪费,因为您增加了查询数量而没有得到任何回报。但是,这种重组实际上可以带来显着的性能优势:

  • 缓存可以更有效。许多应用程序缓存直接映射到表的“对象”。在此示例中,如果带有标签的对象mysql已被缓存,则应用程序将跳过第一个查询。如果在缓存中找到ID为123、567或908的帖子,则可以将其从IN()列表中删除。查询缓存也可以从该策略中受益。如果只有一个表经常更改,则分解联接可以减少缓存失效的次数。
  • 单独执行查询有时可以减少锁争用
  • 通过在应用程序中进行联接,可以通过将表放置在不同的服务器上来更轻松地扩展数据库。
  • 查询本身可以更有效。在此示例中,使用IN()列表而不是联接使MySQL可以对行ID进行排序,并比联接可能获得的查询更优化地检索行。
  • 您可以减少冗余的行访问。在应用程序中进行联接意味着只检索每行一次,而查询中的联接本质上是一种非规范化,可能会重复访问相同的数据。出于相同的原因,这种重组也可能会减少总的网络流量和内存使用量。
  • 在某种程度上,您可以将此技术视为手动实现哈希联接,而不是MySQL用于执行联接的嵌套循环算法。哈希联接可能更有效。

因此,当您缓存和重用早期查询中的大量数据,在多个服务器上分布数据,用IN()列表替换联接或联接多次引用同一张表时,dos联接在应用程序中的效率更高。

观察

我喜欢第一个要点,因为InnoDB在交叉检查查询缓存时有点笨拙。

至于最后一个要点,我在2013年3月11日写了一篇帖子(描述了嵌套循环算法)(JOIN条件和WHERE条件之间有执行差异吗?)。阅读完之后,您将看到联接分解的效果如何。

至于本书中的所有其他要点,开发人员确实将性能作为底线。有些依赖于外部手段(应用程序外部)来提高性能,例如使用快速磁盘,获得更多的CPU /核心,调整存储引擎以及调整配置文件。其他人会妥协并编写更好的代码。有些人可能会诉诸于在存储过程中对所有商业智能进行编码,但仍未应用联接分解(请参阅反对将逻辑放在数据库层的论点或将逻辑放在数据库层的论点?以及其他文章)。这完全取决于每个开发人员商店的文化和承受能力。

有些人可能会对性能感到满意,因此不再接触代码。其他人根本没有意识到,如果他们尝试加入组合,就会收获很多好处。

对于那些愿意...的开发商

试试看 !!!


3
至于关于更改为3个查询的链接...我了解并尊重Baron,Vadim和Peter,但我不同意这种误导性建议。大多数赞成拆分的论点很少见,因此不值得一提。坚持使用JOIN进行单个查询,然后让我们对其进行改进。
瑞克·詹姆斯

2
@RickJames我同意您的评论精神。多年来,我看到一些人参加了分解工作,而另一些人却失败了。即使使用适当的SQL技能,如果连接分解未正确完成,它也会对您不利。在我目前的雇主那里,许多部门都喜欢扩大规模和扩大规模,特别是在涉及到遗留代码并且财大气粗的情况下。对于那些有鱼子酱口味但鸡蛋沙拉预算有限的人,加入分解可能值得冒险,但必须正确进行。
RolandoMySQLDBA

如果有时间和权利,我很想看看这在Oracle环境中如何工作。
里克·亨德森

另一种更快的方式是,如果您要进行订购,订购一个较小的列表要比订购一个较大的列表总的计算量少。
伊万·西洛基

24

Postgres的(也可能任何RDBMS程度相近,MySQL的程度较轻),较少的查询几乎都是快。

在大多数情况下,解析和计划多个查询的开销已经超过了任何可能的收益。

更不用说要在客户端中完成其他工作了,将结果结合起来通常会慢。RDBMS专门从事这种任务,并且操作基于原始数据类型。不会强制转换text为中间结果或转换为客户端的本机类型,这甚至可能导致正确(或不正确!)结果的减少。考虑浮点数...

您还将在数据库服务器和客户端之间传输更多数据。对于一手充满价值的手来说,这可以忽略不计,或者产生巨大的变化。

如果多个查询意味着多次往返数据库服务器,则您还将收集网络延迟和事务开销(甚至可能是连接开销)的数倍。大,大损失。

根据您的设置,仅网络延迟可能要比所有其他时间花费几个数量级。

关于SO的相关问题:

对于大型,长期运行的查询可能会有一个转折点,因为事务会在途中收集数据库行上的锁。非常大的查询可能会长时间保留许多锁,这可能会导致与并发查询产生冲突


出于好奇,您认为很大吗?
Sablefoste

@Sablefoste:很大程度上取决于您的访问模式。一个关键点是并发事务开始排队,等待释放锁,或者如果您积累了足够的锁以占用大量资源。或者,如果您的查询运行时间足以干扰自动真空...
Erwin Brandstetter

但是,如果我们采取某种典型的情况-使用外部联接并为“父”表返回大量冗余数据的查询,则必须由应用(很可能是一些ORM库)对应用程序进行解析和排序小选择首先获取所有必需的ID,然后再使用IN()而不是外部联接进行另一个小选择?第二种方法是否会更有效(考虑数据库和应用程序消耗的CPU和通信带宽)?
JustAMartin

1
@JustAMartin:这听起来像是一种查询,它在由RDBMS的查询计划者处理时几乎可以肯定会更快-假设查询正确。关于returns lots of redundant data for "parent" table:为什么要返回冗余数据?只返回您需要的数据。
Erwin Brandstetter

1
通过外部联接,RDBMS从父表返回的数据对于每个联接的子代都是重复的,这意味着一些网络和内存开销,然后在ORM工具中进行一些额外的解析以丢弃重复的父代值,并仅保留一个具有n个子代的父代。因此,通过单个查询,我们可以节省RDBMS查询计划程序的有效工作,减少网络(或本地管道)请求,但会丢失其他不必要的有效负载并在ORM库中转移数据。我想,这和往常一样-在优化之前先进行测量。
JustAMartin
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.