Как читать фактический план SQL Server по измерениям
Сопоставляйте строки, чтения и предикаты фактического плана, чтобы обоснованно менять запросы SQL Server и проверять результат оптимизации.
Диаграмма плана словно предлагает простой способ диагностики: найти самый большой процент и убрать соответствующий оператор. Но проценты показывают оценку стоимости оптимизатором, а не измеренную долю времени текущего выполнения. Полезное исследование связывает конкретный запрос, поток строк и фактическое потребление ресурсов.
Зафиксировать условия выполнения
Сохраните текст SQL, значения и типы параметров, уровень совместимости базы и существенные настройки сессии. Тот же текст в другом окне с другими параметрами уже представляет другой эксперимент. Включите фактический план в SSMS или используйте STATISTICS XML. Получение такого плана выполняет инструкцию. Для пакета с изменением данных это не безопасный аналог просмотра оценочного плана.
Следующий пример сравнивает одну агрегацию до и после создания индекса. Включите фактический план перед запуском в тестовой сессии.
CREATE TABLE #PlanOrders
(
OrderId int NOT NULL PRIMARY KEY,
CustomerId int NOT NULL,
Amount decimal(12,2) NOT NULL
);
;WITH N AS
(
SELECT TOP (10000)
CONVERT(int, ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS n
FROM sys.all_objects AS a CROSS JOIN sys.all_objects AS b
)
INSERT #PlanOrders (OrderId, CustomerId, Amount)
SELECT n, n % 100, CONVERT(decimal(12,2), n % 250)
FROM N;
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
SELECT SUM(Amount) AS Total
FROM #PlanOrders WHERE CustomerId = 42;
CREATE INDEX IX_PlanOrders_Customer
ON #PlanOrders (CustomerId) INCLUDE (Amount);
SELECT SUM(Amount) AS Total
FROM #PlanOrders WHERE CustomerId = 42;
SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
DROP TABLE #PlanOrders;
Оба SELECT должны вернуть одинаковую сумму. Сравните логические чтения в сообщениях и операторы доступа. Для второго запроса доступен покрывающий индекс по CustomerId и Amount, но фиксируйте реальный выбор оптимизатора, а не обещайте появление определенной картинки.
Таблица намеренно небольшая, чтобы пример оставался управляемым. В рабочей базе селективность и ширина строк могут изменить выбор. Отделяйте подготовку от измерения: построение индекса тоже присутствует в пакете и потребляет ресурсы, но его план не описывает SELECT. Сохраните оба плана вместе с показателями. Не очищайте общий кэш планов или данных сервера ради искусственно чистого эксперимента.
Найти первое существенное расхождение
Прослеживайте данные от операторов доступа к результату. На важных переходах сравнивайте оценочное и фактическое количество строк. Если фильтр должен был вернуть 100 строк, а вернул 200 000, дорогой join дальше может быть лишь следствием ранней ошибки. Исправление только последней сортировки оставит причину на месте.
Различайте прочитанные и переданные дальше строки. Seek может найти широкий диапазон, а затем отбросить почти все строки остаточным предикатом. Название оператора само по себе не доказывает малый объем работы. Откройте свойства, рассмотрите условия поиска и остаточные условия, сравните оба количества. Полное чтение небольшой таблицы иногда дешевле множества случайных обращений.
Для Nested Loops важно число запусков внутреннего оператора. Дешевое действие, повторенное 100 000 раз, может определять общий расход ресурсов. Проверьте, относится ли показанная оценка к одному запуску или ко всем сразу. Особенно внимательно сравнивайте данные параллельных планов, где показатели могут быть разделены по потокам.
Lookup, Sort, Spool и Hash Join не являются ошибками по определению. Выясните назначение оператора и объем выполненной работы. Индекс, устраняющий дополнительные поиски, увеличивает объем хранения и стоимость изменений. Spool может экономить повторную обработку, а сортировка может обеспечивать необходимый пользователю порядок.
Проверять конкретную гипотезу
Сформулируйте проверяемую причину: остаточный предикат читает слишком широкий диапазон, неравномерный фильтр портит оценку или лишние столбцы увеличивают ширину сортировки. Сделайте одно связанное изменение. Повторите характерные наборы параметров и сравните правильность результата, чтения, CPU, время и поведение при одновременной нагрузке.
Предупреждения времени выполнения требуют внимания, но не заменяют диагноз. Сброс данных на диск может существенно мешать нагруженной системе и почти не влиять на маленький разовый запрос. Предложение отсутствующего индекса относится к определенному контексту оптимизации. Перед созданием сравните его с существующими индексами и стоимостью записи.
Наконец, сопоставьте работу сервера с задержкой пользователя. Небольшие CPU и чтения не исключают блокировки, ожидания памяти или медленного получения строк клиентом. Фактический план дополняет статистику ожиданий и сквозное измерение времени.
Для командной работы сохраняйте также краткую запись о проверенной гипотезе: какой оператор исследовали, какие параметры использовали и какое изменение подтвердило или опровергло причину. Один скриншот без контекста быстро теряет ценность. Полный исходный план и измерения позволяют при следующей регрессии отличить возврат старой проблемы от появления нового ограничения.
Техническая документация: Microsoft Learn: Actual execution plans · Microsoft Learn: Showplan operators · Microsoft Learn: STATISTICS IO.