Улюблені трюки з налаштування ефективності [закрито]


126

Коли у вас є запит або збережена процедура, яка потребує налаштування продуктивності, які з перших речей ви спробуєте?


Ось кілька прийомів оптимізації запитів SQL Server
SQLMenace

Я згоден, що це не конструктивно і його можна шукати в Google, але чому він має 118 uv ?! :)
ФЛІКЕР

Відповіді:


114

Ось зручний перелік речей, які я завжди дарую комусь, запитуючи мене про оптимізацію.
Ми в основному використовуємо Sybase, але більшість порад застосовуватимуться в усіх напрямках.

Наприклад, SQL Server постачається з безліччю бітів моніторингу / налаштування продуктивності, але якщо у вас немає нічого подібного (а може бути, навіть якщо ви робите), я б розглядав наступне ...

99% проблем, які я бачив, викликані введенням занадто багато таблиць в об'єднання . Виправлення цього полягає в тому, щоб зробити половину з'єднання (з деякою з таблиць) та кешувати результати у тимчасовій таблиці. Потім виконайте решту запитів, приєднавшись до цього тимчасового столу.

Контрольний список оптимізації запитів

  • Запустіть ОНОВЛЕННУ СТАТИСТИКУ на базових таблицях
    • У багатьох системах це виконується як заплановане щотижневе завдання
  • Видалити записи з базових таблиць (можливо, архівуйте видалені записи)
    • Подумайте, що робити це автоматично раз на день або раз на тиждень.
  • Перебудуйте індекси
  • Поновіть таблиці (вихідні дані / вхідні дані)
  • Вивантажте / перезавантажте базу даних (різко, але може усунути корупцію)
  • Побудуйте новий, більш відповідний індекс
  • Запустіть DBCC, щоб перевірити, чи можливі пошкодження в базі даних
  • Замки / тупики
    • Переконайтеся, що в базі даних немає інших процесів
      • Особливо DBCC
    • Використовуєте блокування рівня рядків чи сторінок?
    • Блокуйте таблиці виключно перед початком запиту
    • Перевірте, чи всі процеси отримують доступ до таблиць в одному порядку
  • Чи правильно використовуються індекси?
    • Приєднання використовуватиме індекс лише у тому випадку, якщо обидва вирази точно однакового типу даних
    • Індекс буде використаний лише у тому випадку, якщо в запиті збігаються перші поля в індексі
    • Чи використовуються кластерні індекси, де це доречно?
      • Дальність діапазону
      • WHERE поле між value1 та value2
  • Малі приєднання - це приємні об’єднання
    • За замовчуванням оптимізатор розглядатиме лише 4 таблиці за один раз.
    • Це означає, що в об'єднанні з більш ніж 4 таблицями є хороший шанс вибрати неоптимальний план запитів
  • Розбийте приєднання
    • Ви можете розірвати приєднання?
    • Попередньо виберіть сторонні ключі до тимчасової таблиці
    • Виконайте половину приєднання та покладіть результати у тимчасову таблицю
  • Ви використовуєте правильний вид тимчасової таблиці?
    • #tempтаблиці можуть працювати набагато краще, ніж @tableзмінні з великими обсягами (тисячі рядків).
  • Вести зведені таблиці
    • Побудувати за допомогою тригерів на нижчих таблицях
    • Будуйте щодня / щогодини / тощо
    • Побудувати спеціально
    • Побудовуйте поступово або відривайте / відновлюйте
  • Подивіться, який план запитів увімкнено SET SHOWPLAN
  • Подивіться, що насправді відбувається з SET STATS IO ON
  • Формуйте індекс за допомогою прагми: (індекс: myindex)
  • Примушуйте замовляти таблицю, використовуючи SET FORCEPLAN ON
  • Параметр Sniffing:
    • Розбийте збережену процедуру на 2
    • виклик proc2 від proc1
    • дозволяє оптимізатору вибрати індекс у proc2, якщо @parameter було змінено на proc1
  • Чи можете ви вдосконалити обладнання?
  • У який час ти біжиш? Чи тихіший час?
  • Чи працює сервер реплікації (або інший режим без зупинки)? Ви можете призупинити це? Запустити його, наприклад. щогодини?

2
до якого шматочка ви задумуєтесь?
AJ.

2
Це круті речі, але я б хотів, щоб у вас були посилання на деякі претензії. Наприклад: Я ніколи не чув, щоб оптимізація розглядала лише 4 таблиці за час з'єднання. Я не розумію, як це могло бути правильним. Чи можете ви надати конкретні посилання на це зокрема? Я хотів би побачити, де ти це дістаєш.
ШелдонH

19
  1. Майте досить гарне уявлення про оптимальний шлях запуску запиту в голові.
  2. Перевірте план запитів - завжди.
  3. Увімкніть STATS, щоб ви могли перевірити продуктивність IO та процесора. Зосередьтеся на зменшенні цих цифр, не обов'язково на час запиту (оскільки на це може впливати інша діяльність, кеш-пам'ять тощо).
  4. Шукайте велику кількість рядків, що надходять до оператора, але невеликі числа виходять. Зазвичай, індекс допоможе, обмеживши кількість рядків, що надходять (що зберігає читання з диска).
  5. Спершу зосередьтеся на найдешевшій піддеревці витрат. Зміна цього піддерева часто може змінити весь план запитів.
  6. Поширені проблеми, які я бачив:
    • Якщо об'єднань багато, інколи Sql Server вирішить розширити приєднання, а потім застосує пункти WHERE. Зазвичай ви можете це виправити, перемістивши умови WHERE у пункт JOIN, або в отриману таблицю з умовами. Погляди можуть викликати ті самі проблеми.
    • Субоптимальні приєднання (LOOP проти HASH проти MERGE). Моє правило - використовувати LOOP-з'єднання, коли верхній ряд має дуже мало рядків порівняно з нижнім, МЕРЕЖЕ, коли множини приблизно рівні і впорядковані, і HASH для всього іншого. Додавання підказки про приєднання дозволить вам перевірити свою теорію.
    • Параметр нюхає. Якщо спочатку ви застосували збережений протокол з нереальними значеннями (скажімо, для тестування), тоді план кешованих запитів може бути неоптимальним для ваших виробничих значень. Запустившись знову з RECOMPILE, це слід перевірити. Для деяких збережених програм, особливо тих, що стосуються різного діапазону (скажімо, усі дати між сьогоднішнім та вчорашнім днем, - що спричинило б за собою INDEX SEEK - або всі дати між минулим і поточним роком), що було б краще за допомогою сканування INDEX ) вам, можливо, доведеться запускати його з РЕКОМПЛІЮ щоразу.
    • Поганий відступ ... Гаразд, тому сервер Sql з цим не має проблеми - але я впевнений, що неможливо зрозуміти запит, поки я не виправлю форматування.

1
+1 для включення поганого відступу. Форматування є ключовим! :)
mwigdahl

18

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

  • Для середовищ з високим IO переконайтесь, що ваші диски мають або RAID 10, або RAID 0 + 1, або якусь вкладену реалізацію raid 1 і raid 0.
  • Не використовуйте диски менше 1500 Кб.
  • Переконайтеся, що ваші диски використовуються лише для вашої бази даних. IE немає реєстрації без ОС.
  • Вимкніть функцію автоматичного зростання або подібну функцію. Нехай база даних використовує всі сховища, які очікуються. Не обов’язково те, що зараз використовується.
  • спроектуйте схему та індекси для запитів типу.
  • якщо це таблиця типу журналу (лише вставка) і повинна бути в БД, не індексуйте її.
  • якщо ви робите багато звітів (комплекс вибирається з багатьма приєднаннями), тоді вам слід поглянути на створення сховища даних зі схемою зірки або сніжинки.
  • Не бійтеся повторювати дані в обмін на продуктивність!

8

CREATE INDEX

Переконайтеся, що є ваші індекси WHEREта JOINпункти. Це значно пришвидшить доступ до даних.

Якщо ваше оточення - це дані або склад даних, індекси повинні бути рясні для практично будь-якого можливого запиту.

У транзакційному середовищі кількість індексів має бути нижчою, а їх визначення більш стратегічними, щоб підтримка індексу не затягувала ресурси. (Підтримка індексу - це коли листя індексу потрібно змінити, щоб відобразити зміну базової таблиці, як INSERT, UPDATE,і DELETEоперації.)

Також пам’ятайте про порядок полів в індексі - чим більш вибірковим (вища кардинальність) є поле, тим раніше в індексі воно повинно з’являтися. Наприклад, скажіть, що ви запитуєте про вживані автомобілі:

SELECT   i.make, i.model, i.price
FROM     dbo.inventory i
WHERE    i.color = 'red'
  AND    i.price BETWEEN 15000 AND 18000

Ціна, як правило, має вищу кардинальність. Доступно лише кілька десятків кольорів, але цілком можливо тисячі різних запитуваних цін.

З цих варіантів вибору індексу idx01передбачено швидший шлях до задоволення запиту:

CREATE INDEX idx01 ON dbo.inventory (price, color)
CREATE INDEX idx02 ON dbo.inventory (color, price)

Це тому, що менше автомобілів задовольнять цінову точку, ніж вибір кольору, даючи механізму запиту набагато менше даних для аналізу.

Мені відомо, що є два дуже схожих індексу, що відрізняються лише польовим порядком для швидкого запиту (ім'я, прізвище) в одному і (прізвище, ім'я) в іншому.


6

Нещодавно я дізнався, що SQL Server може оновлювати локальні змінні, а також поля, в операторі оновлення.

UPDATE table
SET @variable = column = @variable + otherColumn

Або більш читаною версією:

UPDATE table
SET
    @variable = @variable + otherColumn,
    column = @variable

Я використовував це для заміни складних курсорів / з'єднань при здійсненні рекурсивних обчислень, а також отримав багато продуктивності.

Ось деталі та приклад коду, який зробив фантастичні покращення продуктивності: http://geekswithblogs.net/Rhames/archive/2008/10/28/calculating-running-totals-in-sql-server-2005---the-optimal. асп


5

Якщо припустити MySQL тут, використовуйте EXPLAIN, щоб дізнатися, що відбувається з запитом, переконайтесь, що індекси використовуються максимально ефективно та спробуйте усунути різновиди файлів. Висока продуктивність MySQL: оптимізація, резервне копіювання, реплікація та багато іншого - це чудова книга на цю тему, як і Блог продуктивності MySQL .


3
Це добре для MySQL, але питання було позначене "sqlserver". Все-таки це добре робити. Аналогічне, що потрібно зробити в SSMS - це використовувати "Відображати передбачуваний план виконання" та "Включити фактичний план виконання". Якщо ви можете усунути величезні сканування таблиць і використовувати кластеризовані пошукові покажчики, тоді ви вже на шляху до оптимальної продуктивності.
екзорцо


3

Іноді в SQL Server, якщо ви використовуєте АБО в пункті де, він дійсно зміститься з продуктивністю. Замість використання АБО просто зробіть два варіанти і об'єднайте їх разом. Ви отримуєте однакові результати на швидкості 1000x.


Я бачив цю незрозумілу поведінку.
Есен

2

Подивіться, де пункт - перевірте використання індексів / переконайтесь, що нічого дурного не робиться

where SomeComplicatedFunctionOf(table.Column) = @param --silly

2

Я, як правило, розпочну з об'єднань - я вибиваю кожного із них із запиту один за одним і повторно запускаю запит, щоб отримати уявлення, чи є певне приєднання, з яким я маю проблеми.


2

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

declare @temp table(
    RowID int not null identity(1,1) primary key,
    SomeUniqueColumn varchar(25) not null,
    SomeNotUniqueColumn varchar(50) null,
    unique(SomeUniqueColumn)
)

2

Я зробив звичку завжди використовувати змінні змінні. Можливо, змінні змінні не допоможуть, якщо RDBMS не кешує оператори SQL. Але якщо ви не використовуєте змінні прив'язки, RDBMS не має шансу повторно використовувати плани виконання запитів та проаналізувати оператори SQL. Економія може бути величезною: http://www.akadia.com/services/ora_bind_variables.html . Я працюю в основному з Oracle, але Microsoft SQL Server працює майже так само.

На моєму досвіді, якщо ви не знаєте, використовуєте чи ні, використовуєте змінні прив’язки, ви, мабуть, не так. Якщо мова вашої програми не підтримує їх, знайдіть цю програму. Іноді ви можете виправити запит A, використовуючи прив'язні змінні для запиту B.

Після цього я розмовляю з нашою DBA, щоб дізнатися, що викликає RDBMS найбільше болю. Зауважте, що не слід запитувати "Чому цей запит повільний?" Це як би попросити лікаря вийняти вам додаток. Впевнені, що ваш запит може бути проблемою, але це так само ймовірно, що щось інше піде не так. Як розробники ми схильні мислити з точки зору рядків коду. Якщо лінія повільна, виправте її. Але RDBMS - це дійсно складна система, і ваш повільний запит може бути симптомом набагато більшої проблеми.

Занадто багато підказок щодо налаштування SQL - кумири з вантажних культів. Більшість випадків ця проблема не пов'язана або мінімально пов'язана з використовуваним вами синтаксисом, тому зазвичай найкраще використовувати найчистіший синтаксис, який ви можете. Тоді ви можете почати шукати способи налаштування бази даних (не запит). Налаштуйте синтаксис лише тоді, коли це не вдасться.

Як і будь-яка настройка продуктивності, завжди збирайте змістовну статистику. Не використовуйте час настінних годин, якщо це не налаштування користувача, яке ви налаштовуєте. Замість цього подивіться на такі речі, як час процесора, вилучені рядки та блоки зчитування з диска. Занадто часто люди оптимізуються для неправильної речі.


2

Перший крок: Подивіться план виконання запитів!
TableScan -> поганий
NestedLoop -> Мех попередження
TableScan за NestedLoop -> DOOM!

ВСТАНОВИТИ СТАТИСТИКУ IO НА
ЧАС ВСТАНОВЛЕНОЇ СТАТИСТИКИ


2

Запуск запиту за допомогою NOT (NoLock) на моєму місці - це майже стандартна операція. Кожен, хто потрапив на запити на десятки гігабайт таблиць, не виймається та знімається.


2
Це слід використовувати розумно, а не звично. Блокування - це не зло, просто неправильно зрозуміло.

2

Якщо можливо, перетворіть НЕ в запити, щоб ЛІТИШЕ ВНІШНІ ПРИЄДНАННЯ Наприклад, якщо ви хочете знайти всі рядки в Table1, які не використовуються за допомогою зовнішнього ключа в Table2, ви можете зробити це:

SELECT *
FROM Table1
WHERE Table1.ID NOT IN (
    SELECT Table1ID
    FROM Table2)

Але ви отримуєте набагато кращі показники завдяки цьому:

SELECT Table1.*
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.Table1ID
WHERE Table2.ID is null

1

@ DavidM

Якщо припустити MySQL тут, використовуйте EXPLAIN, щоб дізнатися, що відбувається з запитом, переконайтесь, що індекси використовуються максимально ефективно ...

У SQL Server план виконання отримує те саме - він говорить вам про те, які індекси потрапляють тощо.


1

Проіндексуйте таблицю (и) за нанесеними вами фільтрами


1

Не обов’язково хитрість виконання SQL як така, але певно пов'язана:

Доброю ідеєю було б використовувати memcached, де це можливо, оскільки це було б набагато швидше, просто витягуючи попередньо складені дані безпосередньо з пам'яті, а не отримуючи їх з бази даних. Існує також аромат MySQL, який вбудований в пам'ять (третя сторона).


1

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


1

Я пильную:

  • Розгорніть будь-які цикли CURSOR і перетворіть у набори на основі UPDATE / INSERT на основі набору.
  • Слідкуйте за будь-яким кодом програми, який:
    • Викликає SP, який повертає великий набір записів,
    • Потім у програмі проходить кожен запис і викликає ІП з параметрами для оновлення записів.
    • Перетворіть це в SP, який виконує всю роботу за одну транзакцію.
  • Будь-яке СП, яке здійснює багато маніпуляцій із струнами Це свідчення того, що дані не структуровані правильно / нормалізовано.
  • Будь-які СП, які знову винаходять колесо.
  • Будь-яке СП, яке я не можу зрозуміти, що він намагається зробити протягом хвилини!

1
SET NOCOUNT ON

Зазвичай перший рядок у моїх збережених процедурах, якщо мені насправді не потрібно використовувати @@ROWCOUNT.


2
@@ ROWCOUNT встановлюється в будь-якому випадку. NOCOUNT вимикає заяви "xx рядків, на які це впливає".
Sklivvz

Чи справді це коли-небудь помітно впливає на продуктивність?
JohnFx

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

Кількість відстежується в SQL Server у будь-якому випадку. Будь-яка різниця в продуктивності, яка ви бачите, полягає в тому, що підрахунки повинні переходити по мережі до переднього кінця. Якщо ви робите один SELECT, це не помітно зміниться. Якщо у вас є цикл із 100000 вставками, це набагато додатково по мережі.
Том Н

1

У SQL Server використовуйте директиву про блокування. Це дозволяє виконувати команду select без необхідності чекати - зазвичай інші операції закінчуються.

SELECT * FROM Orders (nolock) where UserName = 'momma'

3
NOLOCK призначений лише для запитів, для яких вас не цікавлять правильні результати
Mark Sowul

1

Видаляйте курсори там, де це не потрібно.


Ага, курсори - це прокляття! ;)
Sklivvz

8
Тьфу. Не кидайте цього некваліфікованого так. Курсори - як гармати. Вони не погані самі по собі, це просто те, що люди роблять із ними справді погані справи.
JohnFx

1

Видаліть виклики функцій у програмах Sprocs, де функція буде викликати багато рядків.

Мій колега використовував виклики функцій (отримуючи lastlogindate від userid як приклад) для повернення дуже широких наборів записів.

Завдання оптимізації я замінив виклики функцій у прорості кодом функції: я отримав багато відростків часу від> 20 секунд до <1.


0
  • Префікс усіх таблиць з dbo. для запобігання рекомпіляції.
  • Перегляньте плани запитів та знайдіть сканування таблиць / покажчиків.
  • У 2005 році прокрутіть погляди керівництва на відсутні індекси.


0

Не префіксуйте імена збережених процедур з "sp_", оскільки всі системні процедури починаються з "sp_", і SQL Server доведеться шукати складніше, щоб знайти вашу процедуру, коли вона буде викликана.


1
Ви насправді орієнтували цей? Якщо SQL Server робить те, що є розумним (використовуючи алгоритм хеш-пошуку для зберігання збереженої програми), це не має значення. Насправді, якщо SQL Server цього не робив, схоже, що продуктивність системи буде смердючою (оскільки, імовірно, вона називає власні програми).
Джон Штауфер

1
Я думаю, що це потрапляє у відро передчасної оптимізації. Це, мабуть, хороша практика уникати плутанини для людей, але як підказка щодо оптимізації ... D-
JohnFx

0

Брудні читання -

set transaction isolation level read uncommitted

Запобігає загиблим блокуванням, коли цілісність транзакцій не є абсолютно необхідною (що зазвичай так)


1
Так, але це може призвести до дивних помилок, яких ДУЖЕ важко знайти.
Грант Джонсон

0

Я завжди переходжу до SQL Profiler (якщо це збережена процедура з великою кількістю рівнів вкладеності) або планувальник виконання запитів (якщо це кілька операторів SQL без вкладення). У 90% часу ви можете негайно знайти проблему за допомогою одного з цих двох інструментів.

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