Как Sql Server Перешел На Использование Неоптимального Плана Запросов Каждые Два-Три Часа

Последние пару дней я работал над интересной проблемой и хотел бы поделиться своим интересным опытом с сообществом.

В чем проблема: Запускаю хранимую процедуру для выборки данных для отчета — она выполняется три секунды, смотрю бой профайлером — пользователи получают те же результаты.

Но проходит три часа и то же хранилище, с теми же параметрами работает 2 минуты, и то же самое для пользователей.

При этом данные в используемые таблицы не вставлялись/удалялись, окружение не менялось и админы не производили никаких настроек.

Локализовано перед запросом:

   

INSERT INTO @table_variable1 SELECT .

FROM dbo.view_with_unions v1 WITH (READUNCOMMITTED) LEFT JOIN @table_variable2 AS t1 ON t1.Code = v1.DirectionDimensionCode LEFT JOIN other_table v2 WITH (READUNCOMMITTED) ON v2.Code = v1.SaleType WHERE .



Я настроил профайлер на планы выполнения и заметил, что по мере увеличения времени выполнения хранилища меняется и план выполнения проблемного запроса.

Уже что-то! Потом я стал более внимательно присматриваться к тому, что меняется в плане реализации.

Оказалось, что план длительного выполнения использует объединения NestedLoop, а план быстрого выполнения — HashMatch. Быстрый план:

Как SQL Server перешел на использование неоптимального плана запросов каждые два-три часа

Медленный (на который SQL Server переключается через 2 часа):

Как SQL Server перешел на использование неоптимального плана запросов каждые два-три часа

Я не хотел просто писать HINT для использования HASH JOIN, потому что.

нужно понимать, почему SQL Server всё равно выбирает неправильный план.

Первая мысль была, что что-то не так со статистикой, но на плане выполнения от профилировщика Actual Number Of Rows было 0, а Estimated Number Of Rows — 1. Итак.

Так что разница не такая большая, чтобы исследовать проблемы со статистикой и кардинальностью.



Как SQL Server перешел на использование неоптимального плана запросов каждые два-три часа

Однако, раз за разом глядя на Actual Number Of Rows = 0, у меня возникали сомнения — неужели ни одна строка не возвращается всегда.

Оказалось, что это не так, профайлер просто перехватывает план выполнения до того, как запрос будет выполнен и Актуальные данные станут известны.

И соответственно, он не может отображать в Реальном количестве строк ничего, кроме нуля.

Хорошо, теперь давайте посмотрим на реальные значения Actual Number Of Rows! Тогда возник вопрос — почему Estimated Number Of Rows всегда равно единице? Ведь индекс используется, у него актуальная статистика.

И значение Estimated Number Of Rows каждый раз равно 1. Но здесь нет никаких сюрпризов — SQL Server не использует статистику, если он начинается с малоизбирательного столбца (то есть, если количество разных значений невелико, например: 0, 1, NULL).

Поэтому я переместил первый столбец в индексном ключе на последнее место.

Предварительно убедившись, что все условия для этих столбцов прописаны в WHERE и перечислены через AND, а значит индекс с его статистикой по-прежнему пригоден для использования.

Диагноз:

  1. Профилировщик не отображает фактическое количество строк в планах выполнения и записывает не «н/д», а ноль.

    Нам нужно помнить об этом!

  2. В нашем случае SQL Server не использует статистику для определения предполагаемого количества строк.

  3. И даже если он начнет использовать статистику, то очень сильно ошибается.

Решение:
  1. Вручную запускаем длинные запросы из профайлера и смотрим на реальное фактическое количество строк.

  2. Вам необходимо предоставить SQL Server возможность использовать статистику по индексу; для этого первый столбец в ключе должен иметь много разных значений (например, не три 0, 1, NULL).

    Потому что если в первом столбце мало различных значений (низкая выборочность), то SQL Server не имеет возможности адекватно прогнозировать количество строк и поэтому не использует такую статистику.

  3. После перестроения индекса вам необходимо обновить статистику с помощью опции С FULLSCAN, чтобы улучшить качество прогнозов Estimated Number Of Rows: ОБНОВИТЬ СТАТИСТИКУ [dbo].

    [ table_from_union_for_view ] С ПОЛНЫМ СКАНИРОВАНИЕМ; ИДТИ

И теперь все запросы выполняются не более чем за 2 секунды, план выполнения используется как смесь предыдущих и смотрим на фактическое количество строк в Management Studio:

Как SQL Server перешел на использование неоптимального плана запросов каждые два-три часа

Но это не все! Эта операционная стратегия по-прежнему будет кэшировать планы выполнения, что имеет как положительную, так и отрицательную сторону.

Положительный: скорость выполнения действительно 1-2 секунды Отрицательный: периодически запрос выполняется примерно 20-40 секунд, а потом снова продолжает выполняться 1-2 секунды.

Это происходит, когда происходит скачок изменения количества строк в проблемном запросе (либо с больших на маленькие, либо с малых на большие).

Но SQL Server дает нам возможность преодолеть и эту проблему! Для этого вы можете использовать опцию OPTION(RECOMPILE), которая будет перестраивать план выполнения при каждом его выполнении.

Это увеличит время выполнения каждого запроса до 3-4 секунд, но исполнений по 20-40 секунд в течение дня не будет. Кстати, OPTION(RECOMPILE) также помогает получить наиболее правильную оценку Cardinality при использовании временных объектов и табличных переменных, что используется при определении Estimated Number of Rows и далее при выборе плана выполнения запроса.

(подробнее о временных объектах и сути опции RECOMPILE в них описано в очень хорошем посте — sqlblog.com/blogs/paul_white/archive/2012/08/15/temporary-tables-in-stored-procedures.aspx ) Здесь нам самим нужно решить, что важнее — чтобы большинство запросов выполнялось за 1-2 секунды или чтобы ни один запрос в течение суток не выполнялся более 20 секунд. Теги: #sqlserver #profiler #cardinality #план выполнения #sql #Microsoft SQL Server

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.