Почему больше (и разное количество) логических операций чтения с опережающим чтением (предварительная выборка)?

8

После создания базы данных tpch в моем SQL Server, я попробовал следующий запрос:

    set statistics io on
    DBCC DROPCLEANBUFFERS;        
    select top 100 * from dbo.lineitem order by l_partkey;

Элемент таблицы line имеет некластеризованный индекс для l_partkey. Я выполнил вышеупомянутые запросы несколько раз и обнаружил, что логические чтения меняются каждый раз:

    Table 'lineitem'. Scan count 1, logical reads 1019, physical reads 4, read-ahead reads 1760, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'lineitem'. Scan count 1, logical reads 1007, physical reads 4, read-ahead reads 1720, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
    Table 'lineitem'. Scan count 1, logical reads 1030, physical reads 4, read-ahead reads 1792, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Из поста здесь: Количество логических чтений варьируется , я знаю, что это может быть вызвано поведением с опережением чтения.

НО именно почему чтение вперед может вызвать больше логических чтений? Как это меняет поведение SQL Server? Как SQL Server может читать больше индексной страницы, так как она в любом случае находится в кеше?

Во всяком случае, я отключил чтение вперед и снова выполнить вышеуказанный запрос. Теперь он сообщает одинаковое количество логических чтений каждый раз. НО логические чтения намного меньше !!

    Table 'lineitem'. Scan count 1, logical reads 404, physical reads 160, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Поэтому мой вопрос заключается в том, почему функция опережающего чтения может привести к большему количеству логических операций чтения?

Из любопытства я попробовал другой запрос без "order by":

    select top 100 * from dbo.lineitem

Вот результат без чтения вперед:

    Table 'lineitem'. Scan count 1, logical reads 5, physical reads 3, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

Вот результат с прочитанным впереди:

    Table 'lineitem'. Scan count 1, logical reads 15, physical reads 2, read-ahead reads 3416, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

У того, кто читает вперед, еще больше логических чтений. Итак, почему?

user3610199
источник

Ответы:

9

План запроса для ORDER BY l_partkeyпримера почти наверняка читает некластеризованный индекс по порядку (с упреждающим чтением), за которым следует поиск по ключу для извлечения непокрытых столбцов.

Оператор Nested Loops Join над Lookup, вероятно, использует дополнительный prefetching ( WithOrderedPrefetch) для Lookup. Смотрите следующую статью, которую я написал для более подробной информации:

Кроме того, как я уже упоминал в ответе на связанные вопросы и ответы, количество операций чтения с опережением зависит от времени и характеристик подсистемы хранения. Те же соображения применимы к предварительной выборке «Уточняющий запрос» при объединении вложенных циклов.

Обратите внимание , что вопросы SQL Server упреждающего чтения для страниц , которые могли бы быть необходимы индексом сканирования, но это не ограничено по TOPспецификации в запросе. Элемент TOPобработчика запросов, тогда как предварительное чтение контролируется механизмом хранения.

Действия являются отдельными: чтение с опережением (и предварительная выборка) выдает асинхронный ввод / вывод для страниц, которые могут понадобиться при сканировании (или поиске).

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

Итак, все сводится к подробному хронированию перекрывающихся операций: обработчик запросов начнет останавливать конвейер выполнения запроса, как только на итераторе Top будет обнаружено необходимое количество строк (100). То, сколько асинхронных операций ввода-вывода (упреждающее чтение или предварительная выборка) будет выполнено или выполнено в этот момент, по существу недетерминировано.

Вы можете отключить предварительную выборку Nested Loops Join с флагом трассировки 8744 для дальнейшего изучения. Это удалит WithOrderedPrefetchсвойство из соединения с вложенными циклами. Я обычно использую OPTION (QUERYTRACEON 8744)по самому запросу. В любом случае вы должны быть уверены, что не используете повторно кэшированный план с предварительной выборкой. Каждый раз очищайте кэш плана или форсируйте запрос с помощью OPTION (RECOMPILE).

Логическое чтение - это простая мера количества страниц кеша, которые были затронуты от имени запроса. Там, где включено опережающее чтение (и / или предварительная выборка), можно коснуться большего (и другого!) Индекса и страниц данных, чтобы выполнить это опережающее чтение или как часть действия предварительной выборки.

Пол Уайт 9
источник