Логическое чтение отличается при доступе к одним и тем же данным большого объекта

26

Вот три простых теста, которые читают одни и те же данные, но сообщают об очень разных логических чтениях:

Настроить

Следующий скрипт создает тестовую таблицу с 100 одинаковыми строками, каждая из которых содержит столбец xml с достаточным количеством данных, чтобы обеспечить его сохранение вне строки. В моей тестовой базе данных длина сгенерированного xml составляет 20 204 байта для каждой строки.

-- 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 read-forward читает 796.

=== Назначить переменной ====
Таблица «XMLTest». Сканирование 1, логическое чтение 3, физическое чтение 1, чтение с опережением 0,
    lob логическое чтение 0, lob физическое чтение 0, lob read-forward читает 0.

=== ВЫБРАТЬ В ====
Таблица «XMLTest». Сканирование 1, логическое чтение 3, физическое чтение 1, чтение с опережением 0,
    lob логическое чтение 300, lob физическое чтение 37, lob read-forward читает 400.

Вопросов

  • Почему LOB читает так по-разному?
  • Наверняка одни и те же данные читались в каждом тесте?
Пол Уайт говорит, что GoFundMonica
источник

Ответы:

27

Не все чтения равны. SQL Server знает, что доступ к данным больших объектов обходится дорого, и старается избегать его, когда это возможно. Существуют также подробные различия в способах чтения данных больших объектов в каждом случае:

Резюме

Числа разные, потому что:

  • Выборка читает LOB кусками размером с пакет
  • Тест присваивания переменной вообще не читает LOB
  • Тест «Выбрать в» считывает LOB на целых страницах

подробность

  1. гладкий SELECT

    Выберите план

    Сканирование кластеризованного индекса не считывает данные больших объектов. Он только назначает дескриптор LOB механизма хранения . Дескриптор не используется, пока элемент управления не вернется к корню плана.

    Содержимое LOB текущей строки считывается в виде блоков размером TDS и передается клиенту. Логическое чтение подсчитывает количество обращений к странице, поэтому:

    Количество зарегистрированных операций чтения равно числу выполненных фрагментированных операций чтения, плюс по одному для каждого перехода страницы LOB.

    Например: логическое чтение считается в начале каждого фрагмента, когда процесс касается страницы, соответствующей текущей позиции потока. Если пакеты меньше, чем страница базы данных (обычный случай), для одной и той же страницы учитывается несколько логических чтений. Если размер пакета был настолько велик, что весь большой объект мог поместиться в одном фрагменте, количество зарегистрированных логических чтений будет равно количеству страниц больших объектов.

  2. Переменная присваивание

    Переменный план

    Сканирование кластеризованного индекса назначает дескриптор большого объекта, как и раньше. В корне плана дескриптор большого объекта копируется в переменную. К самим данным большого объекта никогда не обращаются (нулевое чтение большого объекта), потому что переменная никогда не читается. Даже если бы это было так, это было бы только через последний назначенный дескриптор большого объекта.

    Нет чтения LOB, потому что данные LOB никогда не доступны.

  3. SELECT INTO

    Выбрать в план

    Этот план использует поставщика массовых наборов строк для копирования данных больших объектов из исходной таблицы в новую таблицу. Он обрабатывает полную LOB-страницу при каждом чтении (без потоковой передачи или разбиения на фрагменты).

    Количество логических чтений соответствует количеству страниц больших объектов в тестовой таблице.

Пол Уайт говорит, что GoFundMonica
источник