ВАРІАНТ (РЕКОМПЛІЯ) - завжди швидше; Чому?


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)

Під час запуску тесту з аналізатора запитів я додаю наступні рядки:

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 намагається генерувати план запитів на основі параметрів, коли Pro вперше компілюється. Це може створити план, який не є оптимальним, коли ви починаєте передавати різні, можливо більш реалістичні параметри
Sparky

3
Чи достатньо стислого запиту для переліку тут? Я думаю, що Sparky є правильним, і це, мабуть, пов’язано з нюханням параметрів, у мене була подібна проблема, яка переплутала хека з мене, поки я не прочитав цю чудову статтю: sommarskog.se/query-plan-mysteries.html
Кріс

1
Але в цьому випадку (заради цього тесту) я завжди проходжу однакові параметри. Жоден інший додаток не зміг проникнути та зателефонувати за запитом, використовуючи інші параметри. Дякуємо за статті. Переглянемо.
Чад Декер

2
Це може статися або тому, що воно нюхає значення параметрів і змінних, або тому, що робить більші спрощення. Приклади великих спрощень б руйнується X = @X OR @X IS NULLдо X=@Xі виконує прагнуть бачити тут або натиснувши предикати далі вниз проти зору з віконними функціями
Мартін Сміт

3
Після редагування приклад Аналізатор запитів використовує змінні, а не параметри. значення тих ніколи не нюхається, окрім як 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)була б тут корисною. Я підозрюю, що вам просто потрібно оновити статистику та план виконання. Перебудова статистики може бути важливою частиною роботи DBA в залежності від вашої ситуації. Якщо після оновлення статистики у вас все ще виникають проблеми, я б запропонував розмістити обидва плани виконання.

І щоб відповісти на ваше запитання - так, я б сказав, що для вашого найкращого варіанту найкращим варіантом є перекомпіляція плану виконання кожного разу, коли ви виконуєте запит.


22
Так, sp_updatestats зробив свою справу. Ви потрапили в цвях по голові, коли згадали про запит, який спочатку виконується на столі з 10 записами, а тепер у таблиці є мільйони записів. Це був саме мій випадок. Я не згадував це у пості, бо не вважав, що це має значення. Захоплюючі речі. Знову дякую.
Чад Декер

3
Це єдиний спосіб, коли я знайшов роботу з табличними змінними, тому що SQL завжди думає, що є один єдиний рядок. Коли він містить кілька тисяч рядків, це стає проблемою.
Олексій Жуковський

4
Ще одна цікава деталь: оновлення статистики неявно приводить до недійсності всіх кешованих планів, які використовують цю статистику, але тільки якщо статистика фактично змінилася після дії оновлення . Отже, для сильно перекошених таблиць, які читаються лише для читань, явно OPTION (RECOMPILE)може бути єдиним рішенням явне .
Гроо

141

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

  1. СТАТИСТИКА- Статистика застаріла. База даних зберігає статистику щодо діапазону та розподілу типів значень у різних стовпцях на таблицях та індексах. Це допомагає механізму запитів розробити "План" атаки щодо того, як він буде виконувати запит, наприклад тип методу, який він використовуватиме для зіставлення ключів між таблицями за допомогою хеша або перегляду всього набору. Ви можете зателефонувати за оновленням статистики для всієї бази даних або лише певних таблиць або індексів. Це уповільнює запит від одного запуску до іншого, оскільки, коли статистика застаріла, ймовірний план запиту не є оптимальним для щойно вставлених або змінених даних для того ж запиту (пояснено далі нижче). Можливо, невірно оновлювати статистику відразу у виробничій базі даних, оскільки буде деяке накладне, сповільнене та відставання залежно від кількості даних для вибірки. Ви також можете використовувати повне сканування або вибірку для оновлення статистики. Якщо ви подивитесь на план запитів, ви також можете переглянути статистику щодо таких індексів, використовуючи командуDBCC SHOW_STATISTICS (ім'я таблиці, ім'я індексу) . Це покаже вам розподіл та діапазони клавіш, якими користується план запитів, щоб базувати свій підхід.

  2. ПАРАМЕТР ПІДКЛЮЧЕННЯ - План запитів, який кешується, не є оптимальним для конкретних параметрів, які ви передаєте, навіть якщо сам запит не змінився. Наприклад, якщо ви передаєте параметр, який отримує лише 10 з 1 000 000 рядків, то створений план запитів може використовувати хеш-приєднання, однак якщо параметр, який ви передаєте, буде використовувати 750 000 з 1 000 000 рядків, створений план може бути індексне сканування або сканування таблиці. У такій ситуації ви можете сказати оператору SQL використовувати параметр OPTION (RECOMPILE) або SP для використання з RECOMPILE. Якщо сказати Двигуну, це "План єдиного використання", а не використовувати кешований план, який, ймовірно, не застосовується. Не існує правила, як прийняти це рішення, це залежить від того, як знати, як запит буде використовуватися користувачами.

  3. ІНДЕКСИ - Можливо, що запит не змінився, але зміна в іншому місці, наприклад, видалення дуже корисного індексу, сповільнила запит.

  4. ROWS CHANGED - Рядки, які ви запитуєте, різко змінюються від виклику до виклику. Зазвичай статистика автоматично оновлюється в цих випадках. Однак якщо ви будуєте динамічний SQL або викликаєте SQL в тісному циклі, є можливість використовувати застарілий план запитів на основі неправильної різкої кількості рядків або статистики. Знову в цьому випадку ВАРІАНТ (РЕКОМПЛІЯ) корисний.

  5. ЛОГІКА Її Логіка, Ваш запит вже не ефективний, він відповідав невеликій кількості рядків, але більше не масштабував. Зазвичай це передбачає більш поглиблений аналіз плану запитів. Наприклад, ви більше не можете робити речі масово, але вам потрібно створювати речі і робити менші Commits, або ваш крос-продукт був чудовий для меншого набору, але тепер він займає процесор і пам'ять у міру збільшення масштабів, це може бути справедливо і для використовуючи DISTINCT, ви викликаєте функцію для кожного рядка, ваші ключові збіги не використовують індекс через перетворення типу CASTING або NULLS або функцій ... Тут занадто багато можливостей.

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


2
Дякую, друже. Це відмінна інформація. Я б не зміг зрозуміти вашу відповідь, коли я спочатку розмістив своє запитання, але тепер для мене це має сенс.
Чад Декер

3
PARAMETER SNIFFING - це на сьогодні найбільший показник мого існування. Я навіть не знав про цю команду до невдалого запитання про інтерв'ю. Моїм рішенням нюху параметрів завжди було хешування значень параметрів та додавання "AND {hash} = {hash}", щоб sql завжди відрізнявся для різних значень. Хак, але це спрацювало.
Джеремі Бойд

27

Щоб додати до відмінного списку (наданого @CodeCowboyOrg) ситуацій, коли ОПЦІЯ (РЕКОМПЛІЯ) може бути дуже корисною,

  1. Табличні змінні . Коли ви використовуєте змінні таблиці, не буде заздалегідь складеної статистики для змінної таблиці, що часто призводить до великих відмінностей між оцінними та фактичними рядками в плані запитів. Використання OPTION (RECOMPILE) для запитів зі змінними таблиці дозволяє генерувати план запитів, який має набагато кращу оцінку числа рядків. Я особливо критично використовував змінну таблиці, яка була непридатною для використання, і яку я збирався відмовитися, поки не додав OPTION (RECOMPILE). Час бігу пішло від години до всього декількох хвилин. Це, мабуть, незвично, але у будь-якому випадку, якщо ви використовуєте змінні таблиці та працюєте над оптимізацією, то варто переглянути, чи змінює OPTION (RECOMPILE) зміна.

1
У мене є запит із 5 змінними таблиці. На моїй машині воно виконується більше півгодини. На машині мого колеги він виконує за <1 секунду. Машини мають аналогічне обладнання та ту саму версію SQL Server. Якщо ми обидва додаємо опцію (RECOMPILE), то вона виконується за 2 секунди на обох машинах. У всіх випадках тест на виконання виконується в SSMS. Що може спричинити цю різницю?
Адам

1
Чи можете ви порівняти план виконання для цього на вашій машині та на вашій колезі без опції (перекомпіляція)? Це може показати джерело різниці.
DWright

1
для тимчасових таблиць це така ж ситуація?
Муфлікс

1
@muflix: гарне запитання. Я не вірю, що ефект тимчасових таблиць однаковий, оскільки в них є статистика, і двигун повинен робити автоматичний вибір перекомпіляції, як для інших таблиць, я вважаю (але я не впевнений). Можливо, хтось інший знає з більшою визначеністю.
DWright

2
Статистика у тимчасових таблицях не оновлюється автоматично або перекомпілюється, тому програмісту потрібно це зробити.
Дж. Майкл Вуерт

1

Найперші дії перед налаштуванням запитів - це дефрагментація / відновлення індексів та статистики, інакше ви витрачаєте свій час.

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

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

якщо план стабільний або ви можете його стабілізувати, ви можете виконати пропозицію за допомогою sp_executesql ('sql речення') для збереження та використання фіксованого плану виконання.

якщо план нестабільний, вам потрібно використовувати спеціальний оператор або EXEC ('sql речення') для оцінки та створення плану виконання кожного разу. (або збережена процедура "з перекомпіляцією").

Сподіваюся, це допомагає.


1

Не приховуючи цього питання, але є пояснення, яке, здається, ніхто не вважав.

СТАТИСТИКА - Статистика не доступна або вводить в оману

Якщо все наведене нижче:

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

Тоді сервер sql може бути неправильно припускаючи, що стовпці некорельовані, що призводить до нижчих, ніж очікувалося, оцінок кардинальності для застосування як обмежень, так і невірно обраного плану виконання. Виправленням у цьому випадку було б створення об’єкта статистики, що поєднує два стовпці, що не є дорогою операцією.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.