sys.dm_exec_sessions中的“读取”列实际上指示什么?


10

这似乎是一个非常基本的问题,确实应该如此。但是,作为科学方法的忠实拥护者,我喜欢创建一个假设,然后对其进行检验以查看我是否正确。在这种情况下,我试图更好地理解的输出sys.dm_exec_sessions,更具体地说,是单列“读取”的输出。

SQL Server联机丛书相当干脆地将其指定为:

在此会话期间,由该会话中的请求执行的读取次数。不可为空。

一个人可能会认为这将指示自会话开始以来从磁盘读取的页数,以满足该会话发出的请求。这是我以为我会检验的假设。

logical_reads表中的列定义为:

在会话上执行的逻辑读取数。不可为空。

从使用SQL Server的经验来看,我相信此列反映了从磁盘内存读取的页面数。换句话说,页面的总数曾经由会话,无论这些页面驻留阅读。具有两个提供相似信息的独立列的差异或价值主张似乎是,对于特定会话,人们可以理解从磁盘读取的页面reads与从缓冲区高速缓存读取的页面的比率logical_reads

在测试平台上,我创建了一个新数据库,创建了一个表,该表具有已知数量的数据页,然后在新会话中读取该表。然后,我sys.dm_exec_sessions查看了readslogical_reads专栏对会议的看法。在这一点上,我对结果感到困惑。也许有人可以为我阐明这一点。

测试装备:

USE master;
IF EXISTS (SELECT 1
    FROM sys.databases d 
    WHERE d.name = 'TestReads')
BEGIN
    ALTER DATABASE TestReads SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE TestReads;
END
GO
CREATE DATABASE TestReads;
GO
ALTER DATABASE TestReads SET RECOVERY SIMPLE;
BACKUP DATABASE TestReads TO DISK = 'NUL:'; /* ensure we are in 
                                            simple recovery model */
GO

USE TestReads;
GO

/*
    create a table with 2 rows per page, for easy math!
*/
CREATE TABLE dbo.TestReads
(
    ID INT NOT NULL
        CONSTRAINT PK_TestReads
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
    , SomeData CHAR(4000) NOT NULL
);

/*
    insert 5000 pages of data
*/
INSERT INTO dbo.TestReads (SomeData)
SELECT TOP(10000) o1.name
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
ORDER BY o1.object_id
    , o2.object_id
    , o3.object_id;


/*
    Verify we have 5,000 pages of data, with 10,000 rows.
*/
SELECT o.name
    , p.rows
    , au.total_pages
    , au.used_pages
    , au.data_pages
FROM sys.partitions p
    INNER JOIN sys.objects o ON p.object_id = o.object_id 
    INNER JOIN sys.allocation_units au 
        ON p.hobt_id = au.container_id 
        AND (au.type = 1 or au.type = 0)
WHERE p.index_id = 1
    AND o.name = 'TestReads'
    AND o.type = 'U';

/*
    issue a checkpoint to ensure dirty pages are flushed to disk
*/
CHECKPOINT 30;
DBCC DROPCLEANBUFFERS;
DBCC FREESYSTEMCACHE ('ALL');
DBCC FREEPROCCACHE;
DBCC FREESESSIONCACHE;
GO

/*
    ensure we have no data cached in memory for the TestReads database
*/
USE master;
ALTER DATABASE TestReads SET OFFLINE WITH ROLLBACK IMMEDIATE;
ALTER DATABASE TestReads SET ONLINE;

SELECT DatabaseName = d.name
    , SchemaName = s.name
    , ObjectName = o.name
    , AllocatedMB = COUNT(1) * 8192E0 / 1048576
    , PagesInMemory = COUNT(1)
FROM sys.dm_os_buffer_descriptors dobd
    INNER JOIN sys.allocation_units au 
        ON dobd.allocation_unit_id = au.allocation_unit_id
    INNER JOIN sys.partitions p 
        ON au.container_id = p.hobt_id 
             AND (au.type = 1 OR au.type = 0)
    INNER JOIN sys.objects o ON p.object_id = o.object_id
    INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
    INNER JOIN sys.databases d 
        ON dobd.database_id = d.database_id
WHERE d.name = 'TestReads'
    AND o.name = 'TestReads'
    AND o.type = 'U'
GROUP BY d.name
    , s.name
    , o.name;

上面的第一条select语句显示,实际上该表确实包含10,000行,总共5,025页,已使用的页5,020和5,000个数据页。就像人们期望的那样:

在此处输入图片说明

第二个选择语句确认TestReads表的内存中没有任何内容。

在一个新的session中,我们执行以下查询,并注意session_id:

USE TestReads;

SET STATISTICS IO ON;

SELECT *
FROM dbo.TestReads;

正如人们期望的那样,这会将整个表从磁盘读取到内存中,如的输出所示SET STATISTICS IO ON

(10000 row(s) affected)
Table 'TestReads'. Scan count 1, logical reads 5020, physical reads 3, 
read-ahead reads 4998, lob logical reads 0, lob physical reads 0, lob 
read-ahead reads 0.

第三部分中,我们检查sys.dm_exec_sessions

SELECT des.session_id
    , des.reads
    , des.logical_reads
FROM sys.dm_exec_sessions des
WHERE des.session_id = 57; /* session_id from the 2nd (previous) session */

我希望看到sys.dm_exec_sessions演出至少 5000两个readslogical_reads。las,我看到reads显示为零。 logical_reads确实显示预期读取次数在5,000以上-在我的测试中显示5,020:

在此处输入图片说明

我知道SQL Server TestReads借助sys_dm_os_buffer_descriptorsDMV 将整个表读入内存:

USE TestReads;
GO
SELECT DatabaseName = d.name
    , SchemaName = s.name
    , ObjectName = o.name
    , AllocatedMB = COUNT(1) * 8192E0 / 1048576
    , PagesInMemory = COUNT(1)
FROM sys.dm_os_buffer_descriptors dobd
    INNER JOIN sys.allocation_units au 
        ON dobd.allocation_unit_id = au.allocation_unit_id
    INNER JOIN sys.partitions p 
        ON au.container_id = p.hobt_id 
            AND (au.type = 1 OR au.type = 0)
    INNER JOIN sys.objects o ON p.object_id = o.object_id
    INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
    INNER JOIN sys.databases d 
        ON dobd.database_id = d.database_id
WHERE d.name = 'TestReads'
    AND o.name = 'TestReads'
    AND o.type = 'U'
GROUP BY d.name
    , s.name
    , o.name;

在此处输入图片说明

我究竟做错了什么?

我正在为此测试使用SQL Server 2012 11.0.5343。


进一步的发现:

如果我运行以下命令:

SELECT des.session_id
    , des.reads
    , des.logical_reads
FROM sys.dm_exec_sessions des

reads在创建测试装备的会话中看到784个;但是,所有其他会话在该reads列中均显示为零。

现在,我已经将SQL Server测试实例更新为11.0.6020。但是结果是一样的。


sys.dm_exec_requests将给您几乎与set statistics io on结果相同。
金莎(Kin Shah)

1
SET STATISTICS IO ON在第二届会议上我从表中读取之前,有趣的是报告了3次物理读取和4998次预读。但是,sys.dm_exec_sessions仍然无法在reads专栏中反映出来。
Max Vernon 2015年


1
实际上,从2008年到SQL2016CTP3的所有版本上,我的方法都看到这两列均为零
Martin Smith

1
@MartinSmith和Max:我也一直看到reads字段的某些增量出现延迟。我怀疑它的工作原理与session_space_usage或任何显示每个会话的tempdb使用情况的DMV一样,直到“请求”完成后才会增加。
所罗门·鲁兹基

Answers:


2

我一直以来的理解是,这reads仅是物理的(即从磁盘)并且logical_reads仅是从缓冲池(即从内存)。我对一个较小的表进行了快速测试,该表只有2个数据页和3个页面,我所看到的似乎证实了这两个定义。

可能给您带来不好结果的一件事是您没有清除内存。您应该在两次测试之间运行以下命令,以强制从磁盘重新加载它:

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

我的测试设置如下:

CREATE TABLE dbo.ReadTest (Col1 CHAR(7500) DEFAULT (' '));
INSERT INTO dbo.ReadTest (Col1) VALUES (DEFAULT), (DEFAULT);

然后,我运行以下命令:

SELECT reads, logical_reads FROM sys.dm_exec_sessions WHERE session_id = @@SPID;
SELECT * FROM dbo.ReadTest;

(是的,我在运行DMV的同一会话中进行了测试,但这并没有使该reads字段的结果产生偏差,并且如果没有其他影响,则至少是一致的,如果它确实对该logical_reads字段有所帮助。)

为了进行测试,我将运行DBCC命令,然后运行两个SELECT查询。然后,我会在readslogical_reads字段中看到一个跳跃。我会再次运行SELECT查询,有时还会看到额外的跳转reads

之后,我将多次运行两个SELECT查询,并且在每次增加4个时reads将保持不变logical_reads

然后,我将从运行DBCC重新开始,并看到相同的模式。我做了几次,报告的数字在所有测试运行中都是一致的。


更多信息:

我也在SQL Server 2012 SP2-64位(11.0.5343)上进行测试。

我们尝试了以下DBCC命令,但没有效果:

DBCC FREESYSTEMCACHE('ALL');
DBCC FREEPROCCACHE;
DBCC FREESESSIONCACHE;

大多数时候DBCC DROPCLEANBUFFERS都可以,但是我偶尔看到它仍然在缓冲池中。奇。

当我:

  • DBCC DROPCLEANBUFFERS:读取增加24,而逻辑读取增加52。
  • SELECT [Col1] FROM dbo.ReadTest;再次运行:读取不增加,但是逻辑读取增加6。
  • 在查询文本中添加一个空格并重新运行:读取次数不会增加,但是logical_reads会增加52(就像紧接在之后DBCC DROPCLEANBUFFERS)。

看起来52个逻辑读解释了计划生成和结果,这意味着该计划生成导致了另外46个逻辑读。但是物理读取不会再次上升,但是与52个逻辑读取相同,因为它确实需要进行物理读取,因此logical_reads不包括物理读取reads。我只是想清楚地表明这一点,无论是否在问题中进行了表述或暗示。

但是,我确实注意到一种行为,该行为由于存在表中的数据页而引发(至少一点)sys.dm_os_buffer_descriptors:它被其他进程重新加载。如果您DROPCLEANBUFFERS并立即检查,则它应该消失了。但是请等待几分钟,它会再次显示,但是这次没有所有数据页。在我的测试中,该表具有1个IAM页和4个数据页。执行完之后,所有5页都在缓冲池中SELECT。但是,当它被其他进程重新加载时,它只是IAM页和1个数据页。我以为可能是SSMS IntelliSense,但是我在查询选项卡中删除了对该对象名称的所有引用,但仍然重新加载了该对象名称。


有趣的是,我从测试装备中删除了DBCC DROPCLEANBUFFERS(和其他DBCC DROPxxx命令),因为它们没有任何区别。将数据库设置为脱机状态将删除所有缓冲区以及与数据库关联的所有其他内容。
Max Vernon

与您一样,我对物理读取是相同的理解,逻辑读取是从缓冲池btw开始的。
Max Vernon

我还尝试了以下方法: DBCC FREESYSTEMCACHE ('ALL'); DBCC FREEPROCCACHE; DBCC FREESESSIONCACHE;
Max Vernon 2015年

1
@MaxVernon可能是“保持猜测”功能;-)
所罗门·

2
@MaxVernon,不要忘记CHECKPOUNT在之前的数据库上下文中执行DBCC DROPCLEANBUFFERS
Dan Guzman 2015年
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.