SQL Server 如何在内存中缓存数据

Ale*_*sko 7 sql-server optimization memory cache sql-server-2017

我有几个关于数据缓存如何工作的问题
想象一下我们有以下情况:

服务器重新启动或我们刚刚运行 DBCC DROPCLEANBUFFERS

我们有一个Table150 GB 并且有列A, B, C, D, E
A是聚集索引键,列BC对他们的非聚集索引。

  1. 当我们做

    select top 100 * from Table1
    
    Run Code Online (Sandbox Code Playgroud)

    整个聚集索引(表)是否从磁盘读取到内存,即使我们只需要 100 行?还是只有 100 行(它们的数据页)从磁盘读取到内存?

  2. 与非聚集索引相同,当我们这样做时

    select top 100 * from Table1 where column B = 'some value'
    
    Run Code Online (Sandbox Code Playgroud)

    整个非聚集索引+聚集索引是否被加载到内存中?或者只有来自非聚集索引的 100 行和来自聚集索引的 100 行?

Ran*_*gen 9

测试数据

CREATE TABLE dbo.Table1( A INT IDENTITY(1,1) PRIMARY KEY NOT NULL
                     ,B varchar(255),C int,D int,E  int);

INSERT  INTO dbo.Table1 WITH(TABLOCK)
(B,C,D,E)

SELECT TOP(1000000) 'Some Value ' + CAST((ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) % 400) as varchar(255))-- 400 different values
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
,ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2;

CREATE INDEX IX_B
On dbo.Table1(B);
CREATE INDEX IX_C
On dbo.Table1(C);
Run Code Online (Sandbox Code Playgroud)

第一季度

当我们执行“select top 100 * from Table1”时 - 整个聚集索引(表)正在从磁盘读取到内存,即使我们只需要 100 行?或者只有 100 行(它们的数据页)从磁盘读取到内存?

仅读取聚集索引中的 100 行。结果只有它们的数据页被缓存到内存中。

例子

在我的情况下,它从聚集索引向下读取,列的 1 - 100 个值A

SET STATISTICS IO, TIME ON;
select top 100 * from dbo.Table1
Run Code Online (Sandbox Code Playgroud)

内容如下:

Table 'Table1'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)

4 次逻辑读取 = 4 页

为什么我们仍然看到一个scan count = 1,但4你可能会问,但只有逻辑读取?

因为它正在执行范围扫描

在此处输入图片说明

在此处输入图片说明

以满足顶级运营商。


Q2

与非聚集索引相同,当我们执行“select top 100 * from Table1 where column B = 'some value'”时,整个非聚集索引+聚集索引被加载到内存中?还是只有来自非聚集索引的 100 行 + 来自聚集索引的 100 行?

示例 1

SET STATISTICS IO, TIME ON;
select top 100 * from dbo.Table1
WHERE B='Some Value 200'
Run Code Online (Sandbox Code Playgroud)

您可能希望此处使用非聚集索引,但实际上仍然使用聚集索引:

在此处输入图片说明

清除缓存287逻辑读取和2528预读读取后。

Table 'Table1'. Scan count 1, logical reads 287, physical reads 1, read-ahead reads 2528, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)

我们在从磁盘读取它们之后缓存这些预读读取,然后287从内存读取页面。

预读机制是 SQL Server 的功能,它甚至在查询请求数据之前将数据页带入缓冲区缓存。 来源

如果我们检查缓存的页面:

cached_pages_count  objectname  indexname   indexid
2536    Table1  PK__Table1__3BD019AE039DA497    1
Run Code Online (Sandbox Code Playgroud)

我们也看到了这一点。

因此,在这种情况下,我们将缓存更多页面以更快地满足查询,因为我们正在读取更多行以对它们应用残差谓词:

在此处输入图片说明

但是我们只是从聚集索引中读取这些页面。

示例 2

您可以更改查询以希望使用非聚集索引:

select top 100 B from dbo.Table1
WHERE B='Some Value 200';
Run Code Online (Sandbox Code Playgroud)

它的作用是:

在此处输入图片说明

这再次给出了小范围扫描:

Table 'Table1'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)

此处仅将非聚集索引页加载到内存中。

示例 3

我们可能会通过使用索引提示来强制进行键查找以满足示例 1 中的查询:

SET STATISTICS IO, TIME ON;
select top 100 * 
from dbo.Table1
WITH(INDEX(IX_B))
WHERE B='Some Value 200';
Run Code Online (Sandbox Code Playgroud)

这再次导致一些预读读取和比example1更多的逻辑读取:

Table 'Table1'. Scan count 1, logical reads 684, physical reads 3, read-ahead reads 465, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)

由于我们正在从非聚集索引到聚集索引进行键查找,因此我们将从两个索引中缓存页面:

在此处输入图片说明