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

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

Это тот случай, когда запрос выполняется из Query Analyzer или из моей программы С# через 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 [email protected] and [email protected] 

    union 

    select acctNo,feedDate as date 
    from datafeed_money 
    where [email protected] and [email protected] 

    union 

    select acctNo,feedDate as date 
    from datafeed_jnl 
    where [email protected] and [email protected] 
)t1 
group by t1.acctNo
OPTION(RECOMPILE)

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

declare @feedID int
select @feedID=20

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

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

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

Ответ 1

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

EXEC sp_updatestats

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

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

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

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

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

Ответ 2

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

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

  • СООБЩЕНИЕ ПАРАМЕТРОВ. План запросов, который кэшируется, не является оптимальным для конкретных параметров, которые вы передаете, даже если сам запрос не изменился. Например, если вы передаете параметр, который извлекает только 10 из 1 000 000 строк, тогда созданный план запроса может использовать Hash Join, но если параметр, который вы передаете, будет использовать 750 000 из 1 000 000 строк, созданный план может быть сканирование индекса или сканирование таблицы. В такой ситуации вы можете сказать оператору SQL использовать опцию OPTION (RECOMPILE) или SP для использования WITH RECOMPILE. Чтобы сообщить "Двигатель", это "Единый план использования", а не использовать "Кэшированный план", который, вероятно, не применяется. Нет правила о том, как принять это решение, это зависит от того, как пользователь будет использовать запрос.

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

в качестве примера: создать индекс idx01_datafeed_trans  На datafeed_trans (feedid, feedDate)     ВКЛЮЧИТЬ (acctNo, tradeDate)

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

Если план нестабилен, вам необходимо использовать ad-hoc-инструкцию или EXEC ('sql-предложение') для оценки и создания плана выполнения каждый раз. (или хранимой процедуры "с перекомпиляцией" ).

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