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

Han*_*non 11 sql-server dmv sql-server-2012

这似乎是一个非常基本的问题,而且确实应该如此。然而,作为科学方法的粉丝,我喜欢创建一个假设,然后测试它是否正确。在这种情况下,我试图更好地理解 的输出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;
Run Code Online (Sandbox Code Playgroud)

上面的第一个 select 语句表明,实际上该表确实由 10,000 行组成,总共有 5,025 页、5,020 个使用页和 5,000 个数据页;正如人们所期望的那样:

在此处输入图片说明

第二个 select 语句确认我们在内存中没有TestReads表的任何内容。

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

USE TestReads;

SET STATISTICS IO ON;

SELECT *
FROM dbo.TestReads;
Run Code Online (Sandbox Code Playgroud)

正如人们所料,这会将整个表从磁盘读取到内存中,如以下输出所示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.
Run Code Online (Sandbox Code Playgroud)

第三个会话中,我们检查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 */
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

我知道 SQL ServerTestReads借助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;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我究竟做错了什么?

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


进一步的发现:

如果我运行以下命令:

SELECT des.session_id
    , des.reads
    , des.logical_reads
FROM sys.dm_exec_sessions des
Run Code Online (Sandbox Code Playgroud)

reads在创建测试设备的会话中看到了 784;但是所有其他会话在reads列中显示为零。

我现在已将我的 SQL Server 测试实例更新到 11.0.6020;然而结果是一样的。

Sol*_*zky 3

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

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

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
Run Code Online (Sandbox Code Playgroud)

我的测试设置如下:

CREATE TABLE dbo.ReadTest (Col1 CHAR(7500) DEFAULT (' '));
INSERT INTO dbo.ReadTest (Col1) VALUES (DEFAULT), (DEFAULT);
Run Code Online (Sandbox Code Playgroud)

然后我运行了以下命令:

SELECT reads, logical_reads FROM sys.dm_exec_sessions WHERE session_id = @@SPID;
SELECT * FROM dbo.ReadTest;
Run Code Online (Sandbox Code Playgroud)

(是的,我在运行 DMV 的同一个会话中进行测试,但这并没有扭曲该reads领域的结果,如果没有其他问题,至少是一致的,如果它确实有助于logical_reads。)

为了进行测试,我将运行 DBCC 命令,然后运行两个 SELECT 查询。reads然后我会看到和领域都有跳跃logical_reads。我会再次运行 SELECT 查询,有时我会看到reads.

之后,我会多次运行这两个 SELECT 查询,并且reads会保持不变,而logical_reads每次都会增加 4。

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


更多信息:

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

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

DBCC FREESYSTEMCACHE('ALL');
DBCC FREEPROCCACHE;
DBCC FREESESSIONCACHE;
Run Code Online (Sandbox Code Playgroud)

大多数时候DBCC DROPCLEANBUFFERS确实有效,但我偶尔会看到它仍在缓冲池中。奇怪的。

当我:

  • DBCC DROPCLEANBUFFERS:读取数增加 24,逻辑读取数增加 52。
  • 再次运行SELECT [Col1] FROM dbo.ReadTest;:读取次数没有增加,但逻辑读取次数增加了 6。
  • 在查询文本中添加一个空格并重新运行:读取数没有增加,但逻辑读取数增加了 52(就像 后面的那样DBCC DROPCLEANBUFFERS)。

看起来 52 次逻辑读取说明了计划生成和结果,这意味着计划生成导致了额外的 46 次逻辑读取。但是物理读取不会再次上升,但它与确实需要执行物理读取时的 52 个逻辑读取相同,因此logical_reads不包括物理读取reads。我只是澄清这一点,无论问题中是否明示或暗示。

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

  • @MaxVernon,不要忘记在“DBCC DROPCLEANBUFFERS”之前在数据库上下文中执行“CHECKPOUNT”。 (2认同)