Ale*_*sko 7 sql-server optimization memory cache sql-server-2017
我有几个关于数据缓存如何工作的问题
想象一下我们有以下情况:
服务器重新启动或我们刚刚运行 DBCC DROPCLEANBUFFERS
我们有一个Table150 GB 并且有列A, B, C, D, E。
列A是聚集索引键,列B和C对他们的非聚集索引。
当我们做
select top 100 * from Table1
Run Code Online (Sandbox Code Playgroud)
整个聚集索引(表)是否从磁盘读取到内存,即使我们只需要 100 行?还是只有 100 行(它们的数据页)从磁盘读取到内存?
与非聚集索引相同,当我们这样做时
select top 100 * from Table1 where column B = 'some value'
Run Code Online (Sandbox Code Playgroud)
整个非聚集索引+聚集索引是否被加载到内存中?或者只有来自非聚集索引的 100 行和来自聚集索引的 100 行?
测试数据
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你可能会问,但只有逻辑读取?
因为它正在执行范围扫描:
以满足顶级运营商。
与非聚集索引相同,当我们执行“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)
由于我们正在从非聚集索引到聚集索引进行键查找,因此我们将从两个索引中缓存页面: