物理读取和预读读取之间的区别

Rau*_*DBA 2 sql-server database-internals sql-server-2012 buffer-pool

我试图理解read-ahead reading,但对我来说似乎有点复杂。我在网上搜索并得到以下信息:

阅读页面(微软文档):

预读预期完成查询执行计划所需的数据和索引页,并在查询实际使用这些页之前将它们放入缓冲区缓存。

从对为什么在 SQL Server 中首次执行查询时“物理读取”少于“预读”和“逻辑读取”的回答作者:huntharo 在 Stack Overflow 上:

物理读取 - 查询被阻塞,等待页面从磁盘读取到缓存中以供立即使用。

Read-Ahead Read - 页面在阻塞查询之前被读取,并像所有读取一样被读入缓存。当您扫描索引时,预读是可能的,在这种情况下,可以假定索引中的下一个叶页是需要的,并且可以在查询实际表示需要它们之前为它们启动读取。这允许磁盘在 db 引擎检查先前获取的页面的内容时忙碌。

也许有人可以使用他们自己的解释来澄清上述内容,因为我找不到预读的详细解释。

举个例子,看看statistics io信息:

Table 'TestLarge'. Scan count 1, logical reads 159185, physical reads 348, read-ahead reads 159209
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 7

查询总是从内存中读取数据(逻辑读取)。您的示例查询扫描TestLarge表在其执行期间涉及 159,185 个 8KB 内存页。

在执行期间,SQL Server 会做两件事。

1. 它从属于表的页面中读取数据。

如果所需页面已在内存中,则记录逻辑读取

如果所需的页面不在内存中,则会记录物理读取

  • 该页面从持久存储带入内存。
  • 在此读取完成之前,查询将被阻止
  • 这在您的测试查询中发生了 348 次。
  • 一个逻辑读时,SQL Server处理页面(现在是在内存中),以满足您的查询也被计算在内。

2. 它发出预读。

在扫描操作期间,SQL Server 每隔一段时间就会花一点时间管理预读:

  • SQL Server 收集了一个 8KB 页面的列表,这些页面在不久的将来很可能会被当前操作遇到。您可以将其视为引擎“向前看”接下来页面的当前扫描位置。它根据扫描的类型使用 IAM(分配映射)页面或叶上方的 b 树级别来实现。
  • 此“先行”列表中尚未在内存中的任何页面都将在一个或多个异步读取请求中传递给操作系统。这些都算作预读
  • 操作系统负责将页面读入 SQL Server 内存,并在读取完成时通知 SQL Server。
  • 发出异步读取的 SQL Server 线程不会被阻塞。它可以继续扫描内存中的页面,而操作系统在后台在单独的线程上获取预读页面。
  • 您的测试查询通过预读机制将 159,209 页读入内存。

一个类比

想象有一本书。你只得到索引。这本书的其余部分在当地图书馆。图书馆规定整本书不能借阅,每次访问最多只能从图书馆取50页。

您的任务是按照索引(a 到 z)中引用页面的顺序在家中组装这本书。你不能离开家,但你有一个朋友可以代你去图书馆。

索引中的第一个条目是“土豚”,它出现在本书的第 392 页。

你意识到一次一页地完成这项任务会非常低效,所以你不是把你的朋友送到图书馆看第 392 页,而是按索引顺序阅读 50 个条目,然后把要带到图书馆的页面列表交给你的朋友图书馆。此时您计算了 50 次预读

现在你又回到处理“土豚”了。你面前没有第 392 页,所以你必须等待,什么都不做,直到你的朋友回来。这是物理读取

当您的朋友到达时,您在处理第 392 页时计算逻辑读取

您可以从您朋友带回的其他 49 页开始(计算每页的逻辑读取次数),但是您意识到,如果您在忙于从图书馆获取的页面列表时给您的朋友另一个列表,效率会更高在你面前工作。

每次您将要获取的页面列表发送给您的朋友到图书馆时,您都会计算读次数。每次处理您面前的页面时,您都会计算一次逻辑读取。如果你发现自己没有你需要的下一页(因为你的朋友太慢了),你算一个物理阅读

当您和您的朋友可以有效地重叠您的活动时,整体任务会更快完成。他们可能忙于获取您很快需要的页面,而您则忙于处理您面前的页面。如果这运行良好,您就不必等待您需要的下一页,尽管您确实会花一点时间告诉您的朋友该做什么。