以下是三个读取相同数据,但报告逻辑读取却非常不同的简单测试:

Setup

以下脚本创建了一个包含100个相同行的测试表,每个行包含一个包含足够数据的xml列,以确保将其存储在行外。在我的测试数据库中,每行生成的xml的长度为20204字节。

-- Conditional drop
IF OBJECT_ID(N'dbo.XMLTest', N'U') IS NOT NULL
    DROP TABLE dbo.XMLTest;
GO
-- Create test table
CREATE TABLE dbo.XMLTest
(
    ID integer IDENTITY PRIMARY KEY,
    X xml NULL
);
GO
-- Add 100 wide xml rows
DECLARE @X xml;

SET @X =
(
    SELECT TOP (100) *
    FROM  sys.columns AS C
    FOR XML 
        PATH ('row'),
        ROOT ('root'),
        TYPE
);

INSERT dbo.XMLTest
    (X)
SELECT TOP (100)
    @X
FROM  sys.columns AS C;

-- Flush dirty buffers
CHECKPOINT;


测试

以下三个测试读取了带有以下内容的xml列:


简单的SELECT语句
将xml分配给变量
使用SELECT INTO创建临时表

-- No row count messages or graphical plan
-- Show I/O statistics
SET NOCOUNT ON;
SET STATISTICS XML OFF;
SET STATISTICS IO ON;
GO
PRINT CHAR(10) + '=== Plain SELECT ===='

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SELECT XT.X 
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== Assign to a variable ===='

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

DECLARE @X xml;

SELECT
    @X = XT.X
FROM dbo.XMLTest AS XT;
GO
PRINT CHAR(10) + '=== SELECT INTO ===='

IF OBJECT_ID(N'tempdb..#T', N'U') IS NOT NULL
    DROP TABLE #T;

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SELECT 
    XT.X
INTO #T
FROM dbo.XMLTest AS XT
GO
SET STATISTICS IO OFF;


结果

输出为:

=== Plain SELECT ====
Table 'XMLTest'. Scan count 1, logical reads 3, physical reads 1, read-ahead reads 0, 
    lob logical reads 795, lob physical reads 37, lob read-ahead reads 796.

=== Assign to a variable ====
Table 'XMLTest'. Scan count 1, logical reads 3, physical reads 1, read-ahead reads 0, 
    lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

=== SELECT INTO ====
Table 'XMLTest'. Scan count 1, logical reads 3, physical reads 1, read-ahead reads 0, 
    lob logical reads 300, lob physical reads 37, lob read-ahead reads 400.


问题


>为什么LOB的读数有如此大的差异?
每个测试中肯定都读取了完全相同的数据吗?


#1 楼

并非所有读取均相等。 SQL Server知道访问LOB数据很昂贵,因此在可能的情况下尽量避免访问。在每种情况下,读取LOB数据的方式也有详细的区别:

摘要

数字不同是因为: /> select读取数据包大小的块中的LOB

变量赋值测试根本不读取LOB
“ select into”测试将读取整页中的LOB


详细信息



普通SELECT



聚簇索引扫描可以不读取任何LOB数据。它仅分配一个存储引擎LOB句柄。直到控制权返回到计划的根目录时才使用该句柄。逻辑读取计数页面被触摸的次数,因此:

报告的读取数量等于执行的分块读取数量,加上每次发生LOB页面转换时的计数。

例如:当进程触摸与流的当前位置相对应的页面时,将在每个块的开头对逻辑读取进行计数。在数据包小于数据库页面的情况下(通常情况),对同一页面进行几次逻辑读取。如果数据包大小太大,以至于整个LOB可以容纳一个块,那么报告的逻辑读取数就是LOB页数。


变量分配br />

聚簇索引扫描像以前一样分配一个LOB句柄。在计划的根部,将LOB句柄复制到变量。永远不会访问LOB数据本身(读取零个LOB),因为永远不会读取变量。即使存在,也只能通过最后分配的LOB句柄来进行。

由于从未访问过LOB数据,因此没有LOB读取。



该计划使用批量行集提供程序将LOB数据从源表复制到新表。它在每次读取时都处理完整的LOB页(不进行流式传输或分块)。

逻辑读取的数量与测试表中的LOB页的数量相对应。 br />