为什么要进行更多(以及数量不等的)逻辑读取并预读(预取)?


8

在我的SQL Server中创建tpch数据库后,我尝试了以下查询:

    set statistics io on
    DBCC DROPCLEANBUFFERS;        
    select top 100 * from dbo.lineitem order by l_partkey;

表lineitem在l_partkey上具有非聚集索引。我多次发出上述查询,发现每次的逻辑读取都不同:

    Table 'lineitem'. Scan count 1, logical reads 1019, physical reads 4, read-ahead reads 1760, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'lineitem'. Scan count 1, logical reads 1007, physical reads 4, read-ahead reads 1720, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'lineitem'. Scan count 1, logical reads 1030, physical reads 4, read-ahead reads 1792, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

从这里的帖子:逻辑读取计数各不相同,我知道这可能是由预读行为引起的。

但是究竟为什么提前读会导致更多的逻辑读?它如何改变SQL Server的行为?像SQL Server可能会在缓存中读取更多索引页吗?

无论如何,我禁用了预读功能,然后再次发出上述查询。现在,它每次报告相同数量的逻辑读取。但是逻辑读取要小得多!

    Table 'lineitem'. Scan count 1, logical reads 404, physical reads 160, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

所以我的问题是,为什么预读功能会导致更多和各种逻辑读计数?

出于好奇,我尝试了另一个没有“ order by”的查询:

    select top 100 * from dbo.lineitem

这是未事先阅读的结果:

    Table 'lineitem'. Scan count 1, logical reads 5, physical reads 3, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

预读结果如下:

    Table 'lineitem'. Scan count 1, logical reads 15, physical reads 2, read-ahead reads 3416, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

预读的仍然具有更多的逻辑读物。所以为什么?

Answers:


9

ORDER BY l_partkey示例的查询计划几乎可以肯定地按顺序读取非聚集索引(预读),然后进行关键字查找以检索未覆盖的列。

查找上方的嵌套循环连接运算符可能对查找使用附加的预取(WithOrderedPrefetch)。有关完整的详细信息,请参阅以下文章:

另外,正如我在对链接的“问答”的回答中提到的那样,预读次数取决于定时和存储子系统的特性。同样的注意事项也适用于嵌套循环连接处的查找预取。

需要注意的是SQL Server问题预读该网页被索引扫描是必要的,但是这并不局限于通过TOP在查询规范。的TOP是一个查询处理器元件,而超前读由存储引擎控制。

这些活动非常不同:预读(和预取)为扫描(或查找)可能需要的页面发出异步 I / O。

根据I / O实际完成并向查询处理器提供行的顺序(除其他事项外),实际接触(逻辑读取)或物理读取的页面数可能会有所不同。特别要注意的是,当Lookup延迟预取检查以查看Lookup所需的页面是否已在内存中时,它也有助于逻辑读取。

因此,一切都归结为重叠操作的详细时间安排:只要在Top迭代器中看到了所需的行数(100),查询处理器就会开始关闭查询执行管道。在那时,已经发出或完成了多少个异步I / O(预读或预取)基本上是不确定的。

您可以禁用带有跟踪标记8744的嵌套循环加入预取,以进一步进行研究。这将从WithOrderedPrefetch嵌套循环联接中删除该属性。我通常OPTION (QUERYTRACEON 8744)在查询本身上使用。无论如何,您都需要确保您不会重复使用具有预取功能的缓存计划。每次清除计划缓存,或使用强制重新编译查询OPTION (RECOMPILE)

逻辑读取是代表查询触摸的缓存页面数的简单度量。在启用预读(和/或预取)的情况下,可以触摸更多(且不同!)索引和数据页,以便发出预读或作为预取活动的一部分。

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.