OPTION (RECOMPILE) всегда быстрее; Зачем?

169

Я столкнулся со странной ситуацией, когда добавление OPTION (RECOMPILE)к моему запросу приводит к тому, что он выполняется за полсекунды, в то время как его пропуск приводит к тому, что запрос занимает больше пяти минут.

Это тот случай, когда запрос выполняется из Query Analyzer или из моей программы на C # через SqlCommand.ExecuteReader(). Звонит (или не звонит) DBCC FREEPROCCACHEили DBCC dropcleanbuffersне имеет значения; Результаты запроса всегда возвращаются мгновенно OPTION (RECOMPILE)и без него более пяти минут. Запрос всегда вызывается с одинаковыми параметрами [ради этого теста].

Я использую SQL Server 2008.

Я довольно хорошо пишу на SQL, но никогда раньше не использовал OPTIONкоманду в запросе и не был знаком с концепцией кэшей плана до тех пор, пока не просмотрел сообщения на этом форуме. Насколько я понимаю из постов, OPTION (RECOMPILE)это дорогая операция. По-видимому, это создает новую стратегию поиска для запроса. Так почему же последующие запросы, которые опускаются, OPTION (RECOMPILE)являются такими медленными? Разве последующие запросы не должны использовать стратегию поиска, которая была вычислена на предыдущем вызове, который включал подсказку перекомпиляции?

Весьма необычно иметь запрос, который требует подсказки перекомпиляции при каждом вызове?

Извините за вопрос начального уровня, но я не могу этого сделать.

ОБНОВЛЕНИЕ: меня попросили опубликовать запрос ...

select acctNo,min(date) earliestDate 
from( 
    select acctNo,tradeDate as date 
    from datafeed_trans 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where feedid=@feedID and feedDate=@feedDate 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where feedid=@feedID and feedDate=@feedDate 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

При запуске теста из Query Analyzer я добавляю следующие строки:

declare @feedID int
select @feedID=20

declare @feedDate datetime
select @feedDate='1/2/2009'

При вызове из моей программы на C # параметры передаются через SqlCommand.Parametersсвойство.

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

Чад Декер
источник
3
Какие параметры к запросу? Проверьте эту статью. blogs.msdn.com/b/turgays/archive/2013/09/10/… По сути, SQL пытается создать план запроса на основе параметров при первой компиляции proc. Он может генерировать план, который не является оптимальным, когда вы начинаете передавать другие, возможно, более реалистичные параметры
Sparky
3
Является ли запрос достаточно кратким, чтобы перечислить здесь? Я думаю, что Спарки прав, и это, вероятно, связано с анализом параметров, у меня была похожая проблема, которая приводила
Крис
1
Но в этом случае (ради этого теста) я всегда передаю одни и те же параметры. Никакие другие приложения не могли проникнуть внутрь и вызвать запрос, используя другие параметры. Спасибо за статьи. Будет обзор.
Чад Decker
2
Это может произойти либо потому, что он вынюхивает значения параметров и переменных, либо потому, что он делает большие упрощения. Примеры больших упрощений бы рушится X = @X OR @X IS NULLк X=@Xи выполняющее стремятся видеть здесь или нажав предикаты дальше вниз против зрения с оконными функциями
Мартин Смит
3
После внесения изменений в примере Query Analyzer используются переменные, а не параметры. значение тех, кто никогда не обнюхивается, кроме как с RECOMPILE. В любом случае запишите планы выполнения и посмотрите на различия.
Мартин Смит

Ответы:

157

Есть моменты, когда использование OPTION(RECOMPILE)имеет смысл. По моему опыту, единственно возможный вариант - когда вы используете динамический SQL. Прежде чем вы выясните, имеет ли это смысл в вашей ситуации, я бы порекомендовал пересмотреть статистику. Это можно сделать, выполнив следующее:

EXEC sp_updatestats

А затем воссоздать ваш план выполнения. Это гарантирует, что при создании вашего плана выполнения он будет использовать самую последнюю информацию.

Добавление OPTION(RECOMPILE)перестраивает план выполнения каждый раз, когда выполняется ваш запрос. Я никогда не слышал, чтобы это описывалось как, creates a new lookup strategyно, возможно, мы просто используем разные термины для одного и того же.

Когда создается хранимая процедура (я подозреваю, что вы вызываете ad-hoc sql из .NET, но если вы используете параметризованный запрос, то это в конечном итоге является хранимым процедурным вызовом ) SQL Server пытается определить наиболее эффективный план выполнения для этого запроса. на основе данных в вашей базе данных и параметров, передаваемых в ( сниффинг параметров ), а затем кэширует этот план. Это означает, что если вы создаете запрос, в котором имеется 10 записей в вашей базе данных, а затем выполняете его, когда имеется 100 000 000 записей, кэшированный план выполнения может перестать быть наиболее эффективным.

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

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

Абе Мисслер
источник
22
Да, sp_updatestats сделали свое дело. Вы попали в тупик, когда упомянули запрос, первоначально запущенный для таблицы с 10 записями, а теперь в таблице миллионы записей. Это был именно мой случай. Я не упоминал об этом в посте, потому что я не думал, что это имело значение. Увлекательные вещи. Еще раз спасибо.
Чад Декер
3
Это единственный способ, который я нашел для работы с табличными переменными, потому что SQL всегда думает, что в нем есть одна строка. Когда она содержит несколько тысяч строк, это становится проблемой.
Алексей Жуковский
4
Одна интересная деталь: обновление статистики неявно делает недействительными все кэшированные планы, которые используют эту статистику, но только если статистика фактически изменилась после действия обновления . Так что для сильно искаженных таблиц только для чтения, кажется, что явное OPTION (RECOMPILE)решение может быть единственным решением.
Groo
141

Часто, когда между запросами запускается радикальная разница, я обнаруживаю, что это часто одна из 5 проблем.

  1. СТАТИСТИКА- Статистика устарела. База данных хранит статистику по диапазону и распределению типов значений в различных столбцах таблиц и индексов. Это помогает обработчику запросов разработать «план» атаки для того, как он будет выполнять запрос, например, тип метода, который он будет использовать для сопоставления ключей между таблицами, использующими хэш или просматривая весь набор. Вы можете вызвать обновление статистики для всей базы данных или только для определенных таблиц или индексов. Это замедляет запрос от одного прогона к другому, потому что, если статистика устарела, вполне вероятно, что план запроса не является оптимальным для вновь вставленных или измененных данных для того же запроса (более подробно будет объяснено ниже). Возможно, нецелесообразно немедленно обновлять статистику в производственной базе данных, так как будут некоторые накладные расходы, замедление и отставание в зависимости от объема данных для выборки. Вы также можете использовать полное сканирование или выборку для обновления статистики. Если вы посмотрите на план запроса, вы также сможете просмотреть статистику используемых индексов, например, с помощью командыDBCC SHOW_STATISTICS (имя таблицы, имя индекса) . Это покажет вам распределение и диапазоны ключей, которые план запроса использует для своего подхода.

  2. ИЗМЕНЕНИЕ ПАРАМЕТРА - кэшированный план запроса не является оптимальным для конкретных параметров, которые вы передаете, даже если сам запрос не изменился. Например, если вы передадите параметр, который извлекает только 10 из 1 000 000 строк, то созданный план запроса может использовать Hash Join, однако если передаваемый параметр будет использовать 750 000 из 1 000 000 строк, созданный план может быть сканирование индекса или таблицы. В такой ситуации вы можете указать оператору SQL использовать опцию OPTION (RECOMPILE) или SP для использования с RECOMPILE. Сказать Механизму, что это «План одноразового использования», а не использовать кэшированный план, который, скорее всего, не применяется. Не существует правила о том, как принимать это решение, оно зависит от знания того, как пользователи будут использовать запрос.

  3. ИНДЕКСЫ. Возможно, что запрос не изменился, но изменение в другом месте, например удаление очень полезного индекса, замедлило запрос.

  4. ROWS CHANGED - Строки, которые вы запрашиваете, кардинально меняется от вызова к вызову. Обычно статистика автоматически обновляется в этих случаях. Однако, если вы создаете динамический SQL или вызываете SQL в узком цикле, есть вероятность, что вы используете устаревший план запросов, основанный на неправильном значительном числе строк или статистике. Опять же, в этом случае OPTION (RECOMPILE) полезен.

  5. ЛОГИКА Это логика, ваш запрос больше не эффективен, он подходит для небольшого числа строк, но больше не масштабируется. Обычно это требует более глубокого анализа плана запросов. Например, вы больше не можете делать вещи навалом, но должны разбивать вещи на части и делать меньшие коммиты, или ваш кросс-продукт подходит для меньшего набора, но теперь занимает ЦП и память, поскольку масштабируется больше, это также может быть верно для используя DISTINCT, вы вызываете функцию для каждой строки, ваши совпадения ключей не используют индекс из-за преобразования типа CASTING или NULLS или функций ... Здесь слишком много возможностей.

В целом, когда вы пишете запрос, вы должны иметь представление о том, как определенные данные распределяются в вашей таблице. Например, столбец может иметь равномерно распределенное число различных значений или может быть искажен, 80% времени имеют определенный набор значений, независимо от того, будет ли распределение часто меняться со временем или будет довольно статичным. Это даст вам лучшее представление о том, как создать эффективный запрос. Но также при отладке производительности запросов есть основание для построения гипотезы о том, почему он медленный или неэффективный.

CodeCowboyOrg
источник
2
Спасибо друг. Это отличная информация. Я не смог бы понять ваш ответ, когда я первоначально разместил свой вопрос, но теперь он имеет смысл для меня.
Чад Декер
3
ПАРАМЕТР СНИФФИНГА - безусловно самая большая проклятие для моего существования. Я даже не знал об этой команде до неудавшегося вопроса об интервью. Мое решение для анализа параметров всегда состояло в том, чтобы хэшировать значения параметров и добавлять «AND {hash} = {hash}», чтобы sql всегда был разным для разных значений. Взломать, но это сработало.
Джереми Бойд
27

Чтобы добавить в превосходный список (предоставленный @CodeCowboyOrg) ситуации, в которых OPTION (RECOMPILE) может быть очень полезным,

  1. Переменные таблицы . Когда вы используете табличные переменные, предварительно не будет построена статистика для табличной переменной, что часто приводит к большим различиям между оценочными и фактическими строками в плане запроса. Использование OPTION (RECOMPILE) в запросах с табличными переменными позволяет генерировать план запроса, который имеет гораздо лучшую оценку числа задействованных строк. Я особенно критично использовал табличную переменную, которая была непригодна для использования и которую я собирался отказаться, пока не добавил OPTION (RECOMPILE). Время бега от часов до нескольких минут. Это, вероятно, необычно, но в любом случае, если вы используете табличные переменные и работаете над оптимизацией, стоит посмотреть, имеет ли значение OPTION (RECOMPILE).
DWright
источник
1
У меня есть запрос с 5 табличными переменными. На моей машине он выполняется более получаса. На компьютере моего сотрудника он выполняется за <1 секунду. Машины имеют одинаковое оборудование и ту же версию SQL Server. Если мы оба добавим OPTION (RECOMPILE), он будет выполнен за 2 секунды на обеих машинах. Во всех случаях проверка выполнения проводится в SSMS. Что может быть причиной этой разницы?
Адам
1
Можете ли вы сравнить план выполнения для него на вашем компьютере и на компьютере вашего коллеги без Option (перекомпилировать)? Это может показать источник разницы.
DWright
1
для временных таблиц это та же ситуация?
Muflix
1
@muflix: хороший вопрос. Я не верю, что эффект одинаков для временных таблиц, так как они имеют статистику, и механизм должен делать выбор автоматической перекомпиляции, как для других таблиц, я считаю (но не уверен). Может быть, кто-то еще знает с большей уверенностью.
DWright
2
Статистика во временных таблицах не обновляется и не перекомпилируется автоматически, поэтому программист должен это сделать.
Дж. Майкл Вюрт
1

Самые первые действия перед настройкой запросов - дефрагментировать / перестраивать индексы и статистику, иначе вы теряете время.

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

в качестве примера: создать индекс idx01_datafeed_trans для datafeed_trans (feedid, feedDate) INCLUDE (acctNo, tradeDate)

если план стабилен или вы можете стабилизировать его, вы можете выполнить предложение с помощью sp_executesql («sql предложение»), чтобы сохранить и использовать фиксированный план выполнения.

если план нестабилен, вы должны использовать специальный оператор или EXEC («sql предложение»), чтобы каждый раз оценивать и создавать план выполнения. (или хранимая процедура "с перекомпиляцией").

Надеюсь, поможет.

Кристиан Солервиченс
источник
1

Отвечая на этот вопрос, но есть объяснение, которое, кажется, никто не рассматривал.

СТАТИСТИКА - Статистика недоступна или вводит в заблуждение

Если все следующее верно:

  1. Вероятно, столбцы feedid и feedDate будут сильно коррелированными (например, идентификатор канала более конкретен, чем дата канала, а параметр date является избыточной информацией).
  2. Нет индекса с обоими столбцами в качестве последовательных столбцов.
  3. Не существует созданной вручную статистики, охватывающей оба этих столбца.

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

MonkeyPushButton
источник