У меня есть типичный случай, когда сниффинг параметров приводит к тому, что «плохой» план выполнения попадает в кэш плана, в результате чего последующее выполнение моей хранимой процедуры происходит очень медленно. Я могу «решить» эту проблему с помощью локальных переменных OPTIMIZE FOR ... UNKNOWN
, и OPTION(RECOMPILE)
. Однако я также могу погрузиться в запрос и попытаться оптимизировать его.
Я пытаюсь определить, должен ли я : учитывая ограниченное время, чтобы решить проблемы, я хотел бы знать цену не делать этого. Насколько я понимаю, если я просто придерживаюсь OPTION(RECOMPILE)
, общий эффект заключается в том, что план запроса воссоздается при каждом запуске запроса. Итак, я думаю, что мне нужно знать:
Как узнать стоимость создания плана запроса?
Чтобы ответить на мой собственный вопрос, я прогуглил (например, с помощью этого запроса ), и я просмотрел документацию колонок для dm_exec_query_stats
DMV . Я также проверил окно вывода в SSMS на «Actual Query Plan», чтобы найти эту информацию. Наконец, я искал DBA.SE . Ни один из них не привел к ответу.
Кто-нибудь может сказать мне? Возможно ли найти или измерить время, необходимое для создания плана?
источник
Ответы:
Вы можете посмотреть свойства корневого узла в плане запроса, например:
(скриншот из бесплатного Sentry One Plan Explorer )
Эта информация также доступна при запросе кэша плана, например, с помощью запроса, основанного на следующих отношениях:
Для полной обработки опций, которые у вас есть для обработки таких запросов, смотрите недавно обновленную статью Эрланда Соммарскога .
источник
Предполагая, что «стоимость» выражается в терминах времени (хотя и не уверен, что еще это может быть в смысле ;-), тогда, по крайней мере, вы сможете почувствовать это, выполнив что-то вроде следующего:
Первый элемент, указанный на вкладке «Сообщения», должен быть следующим:
Я бы запустил это как минимум 10 раз и усреднил бы и «ЦП», и «Истекшие» миллисекунды.
В идеале вы должны запустить это в Production, чтобы получить точную оценку времени, но людям редко разрешают очищать кэш плана в Production. К счастью, начиная с SQL Server 2008 стало возможным очистить конкретный план из кэша. В этом случае вы можете сделать следующее:
Однако, в зависимости от изменчивости значений, передаваемых для параметра (ов), вызывающих «плохой» кэшированный план, существует другой метод, который следует рассматривать как промежуточное звено между
OPTION(RECOMPILE)
иOPTION(OPTIMIZE FOR UNKNOWN)
: Динамический SQL. Да, я сказал это. И я даже имею в виду непараметрический динамический SQL. Вот почему.У вас явно есть данные с неравномерным распределением, по крайней мере, с точки зрения одного или нескольких значений входных параметров. Недостатки упомянутых вариантов:
OPTION(RECOMPILE)
будет генерировать план для каждого исполнения , и вы никогда не будете в состоянии извлечь выгоду из любого плана повторного использования, даже если значение параметров , передаваемое снова идентично предыдущее выполнение (ов). Для процедур, которые вызываются часто - раз в несколько секунд или чаще - это спасет вас от случайной ужасной ситуации, но все же оставит вас в ситуации, которая не всегда так велика.OPTION(OPTIMIZE FOR (@Param = value))
создаст план на основе этого конкретного значения, которое может помочь в нескольких случаях, но все же оставит вас открытым для текущей проблемы.OPTION(OPTIMIZE FOR UNKNOWN)
создаст план, основанный на том, что составляет среднее распределение, что поможет некоторым запросам, но повредит другим. Это должно быть то же самое, что и опция использования локальных переменных.Однако динамический SQL, если все сделано правильно , позволит различным передаваемым значениям иметь свои собственные отдельные планы запросов, которые являются идеальными (ну, сколько бы они ни были). Основная стоимость здесь заключается в том, что по мере увеличения разнообразия значений, передаваемых в кэш-памяти, увеличивается количество планов выполнения в кэше, и они занимают память. Незначительные расходы:
необходимость проверки строковых параметров для предотвращения SQL-инъекций
возможно, потребуется настроить сертификат и пользователя на основе сертификатов для поддержания идеальной абстракции безопасности, поскольку для динамического SQL требуются прямые разрешения таблиц.
Итак, вот как я справился с этой ситуацией, когда у меня были прокы, которые вызывались более одного раза в секунду и работали с несколькими таблицами, каждая с миллионами строк. Я пытался,
OPTION(RECOMPILE)
но это оказалось слишком вредным для процесса в 99% случаев, в которых не было проблемы с анализом параметров / плохим кэшированием плана. И, пожалуйста, имейте в виду, что в одном из этих процедур было около 15 запросов, и только 3-5 из них были преобразованы в динамический SQL, как описано здесь; Динамический SQL не использовался, если это не было необходимо для конкретного запроса.Если в хранимой процедуре есть несколько входных параметров, выясните, какие из них используются со столбцами, которые имеют сильно разнородные распределения данных (и, следовательно, вызывают эту проблему), а какие используются со столбцами, которые имеют более равномерное распределение (и не должны вызывая эту проблему).
Создайте строку динамического SQL, используя параметры для входных параметров proc, которые связаны с равномерно распределенными столбцами. Эта параметризация помогает уменьшить результирующее увеличение планов выполнения в кеше, связанном с этим запросом.
Для остальных параметров, связанных с сильно варьируемыми дистрибутивами, их следует объединить в Dynamic SQL в виде литеральных значений. Поскольку уникальный запрос определяется любыми изменениями в тексте запроса, наличие
WHERE StatusID = 1
запроса отличается от запроса и, следовательно, отличается от плана запросаWHERE StatusID = 2
.Если какие-либо входные параметры proc, которые должны быть объединены в текст запроса, являются строками, их необходимо проверить для защиты от SQL-инъекций (хотя это менее вероятно, если передаваемые строки генерируются приложение а не пользователь, но все же). По крайней мере, сделайте так,
REPLACE(@Param, '''', '''''')
чтобы одинарные кавычки стали экранированными.При необходимости создайте сертификат, который будет использоваться для создания пользователя, и подпишите хранимую процедуру таким образом, чтобы прямые разрешения для таблиц были предоставлены только новому пользователю на основе сертификатов, а не пользователям
[public]
или пользователям, которые в противном случае не имели бы таких разрешений. ,Пример процедуры:
источник
OPTION
запроса в мой запрос), и он не причинит мне большого вреда, поскольку этот спрок хорошо используется в интеграционных тестах. - В любом случае: спасибо за ваши идеи!