这会导致两次全表扫描吗?

时间:2017-09-27 09:52:21

标签: mysql sql query-optimization

SELECT P_CODE, P_PRICE
FROM PRODUCT
WHERE P_PRICE >= (SELECT AVG(P_PRICE) FROM PRODUCT);

这个查询(在mysql下)会导致两次全表扫描(来自磁盘)还是优化器会理解它也更快(如果有足够的RAM来保存结果集)只进行一次全表扫描?该表没有索引。

是否可以从mysql中的EXPLAIN命令的输出中读取(以某种方式)此信息?

3 个答案:

答案 0 :(得分:3)

基于对表扫描实际上是什么的误解,这个问题存在缺陷:

  • 表扫描遍历表中的所有行(无论它如何获取这些行)。
  • 它与索引扫描略有不同,因为它适用于"完整行"。而索引扫描要处理的整体数据较少,因为它适用于列的子集。

但问题实际上是在询问物理和逻辑IO之间的区别。

  

(来自磁盘)或者优化器是否也会更快(如果有足够的RAM来保存结果集)

是的,查询将执行2次表扫描。这无法避免:

  • 服务器必须两次处理全套价格。
  • 并且必须先完成AVG(PRICE)的处理才能开始处理WHERE过滤器。

然而,"逻辑"表扫描不一定需要从磁盘读取数据两次。如果所有数据都在内存中,则服务器可以在内存中执行表扫描。因此,虽然第二阶段的处理仍然必须执行表扫描,但通过避免二级磁盘访问可以提高效率。

看一下这个问题,看看如何区分mysql上的逻辑和物理IO:
For a MySQL query, how do you determine physical and logical I/O?

我补充一点,理论上服务器可以选择在第一遍中仅保留内存中的Price列。在这种情况下,它不需要执行全表扫描"在第二次通过。
然而,这在实践中是不可能的,因为将所有数据保存在内存中以用于将来的其他查询...... 他们可能希望处理的任何列

答案 1 :(得分:2)

重新评论:

  

我的假设,在查看查询时,不是优化器应该/将能够确定"此查询读取相同的数据两次,在第一次读取后我将把它放入内存(如果有的话)空间)并使用内存中的数据作为查询的下一部分,而不是要求磁盘两次"

好吧,至少在MySQL的InnoDB引擎中,会发生类似这样的事情。 InnoDB无法直接从磁盘读取页面。它在对每个请求的页面进行数据操作之前将其加载到RAM中。 RAM是一个名为InnoDB buffer pool的预分配区域。这将存储来自磁盘表空间的页面的逐字节副本,以及有关它们的一些元数据。

读取页面后,缓冲池不需要立即从RAM中逐出,除非请求其他页面,并且缓冲池中没有剩余空间。因此,对相同页面的后续请求可能会发现页面已经驻留在RAM中。发生的越多,整体表现就越好。

您的product表中可能包含的数据页数多于缓冲池中的数据页数。在表扫描期间,InnoDB将根据需要逐出页面以加载表的剩余页面集。如果你有一个比你的缓冲池大很多倍的表,你可以想象这会导致相当多的" churn"页面进出。如果你负担得起,为缓冲池分配更多的RAM是提高性能的好方法。

关于缓冲池的所有这些事实都不会改变您的查询将执行两次表扫描的事实。确实,从缓冲池读取页面比从磁盘读取页面更快。你可以试验:

  • 关闭MySQL服务器并重新启动它。此时缓冲池应为空(除非您使用该功能save the buffer pool on shutdown)。
  • 运行您的查询。可能需要几秒钟,因为在使用之前必须从磁盘读取所请求的每个页面。
  • 再次运行相同的查询。它更快!我已经看到过这种差异会使测试中的性能提高4倍。我知道RAM通常比磁盘快几千倍,但I / O速度并不是唯一运行的代码。此外,它还取决于占用磁盘带宽的其他请求以及其他因素。

磁盘速度和RAM速度之间的差异(或多或少)是算术因素。无论您的数据集有多大,速度差异都会带来同样的优势。

索引更重要,因为它们将线性搜索O(n)转换为B树搜索O(log 2 n)。随着数据集变大,其优势变得更加显着。这就是为什么如此强调analyzing complexity of algorithms in computer science

答案 2 :(得分:0)

请解释如何只使用一次表格扫描即可完成此操作。这并不明显。

使用AVG()函数通常会导致两次完整扫描。如果您有索引,则一次或两次扫描可能会使用索引。