访问相同的LOB数据时逻辑读取不同


26

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

设定

下面的脚本创建一个包含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列:

  1. 一个简单的SELECT声明
  2. xml分配给变量
  3. 使用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;

结果

输出为:

===普通SELECT ====
表“ XMLTest”。扫描计数1,逻辑读取3,物理读取1,预读读取0,
    lob逻辑读取795,lob物理读取37,lob提前读取796。

===分配变量====
表“ XMLTest”。扫描计数1,逻辑读取3,物理读取1,预读读取0,
    lob逻辑读取为0,lob物理读取为0,lob提前读取为0。

===选择进入====
表“ XMLTest”。扫描计数1,逻辑读取3,物理读取1,预读读取0,
    lob逻辑读取300,lob物理读取37,lob提前读取400。

问题

  • 为什么LOB的读法如此不同?
  • 是否在每次测试中都读取了完全相同的数据?

Answers:


27

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

摘要

数字不同是因为:

  • 选择以分组大小的读取LOB
  • 变量赋值测试根本不读取 LOB
  • “选择进入”测试将读取整个页面中的LOB

详情

  1. 平原 SELECT

    选择方案

    聚集索引扫描不会读取任何LOB数据。它仅分配一个存储引擎LOB 句柄。直到控制权返回到计划的根目录时才使用该句柄。

    当前行的LOB内容以TDS数据包大小的块读取,并流传输到客户端。逻辑读取计数页面被触摸的次数,因此:

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

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

  2. 变量分配

    可变计划

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

    没有LOB读取,因为从不访问LOB数据。

  3. SELECT INTO

    选择进入计划

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

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

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.