План поганих запитів SQL Server 2016 блокує БД раз на тиждень


16

Раз на тиждень, протягом останніх 5 тижнів, приблизно в один і той же час доби (рано вранці, можливо, грунтується на активності користувачів, коли люди починають його використовувати), SQL Server 2016 (AWS RDS, дзеркальне відображення) починає відмічати багато часу запити.

ОНОВЛЕННЯ СТАТИСТИКА на всіх таблицях завжди виправляє її негайно.

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

Востаннє я ввімкнув магазин запитів, щоб дізнатися, чи зможу я знайти конкретний план запиту / запиту. Я думаю, що мені вдалося звузити його до одного:

Поганий план запитів

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

Поганий план запитів робив показник сканування (на столі, що містить лише 10k рядків). Інші плани запитів, які повернулися в мілісекундах, використовувались для того ж сканування. Найновіший план запитів після створення нового індексу лише прагне. Але навіть без цього індексу 99% часу він повертався протягом декількох мілісекунд, але тоді, щотижня, це займе> 40 секунд.

Це почалося після переходу на SQL Server 2016 з 2012 року.

DBCC CHECKDB не повертає помилок.

  1. Чи вирішить новий індекс проблему, змусивши її ніколи не вибирати поганий план?
  2. Чи варто «змушувати» план, який зараз добре працює?
  3. Як зробити так, щоб це не трапилося з іншим запитом / планом?
  4. Це симптом більшої проблеми?

Щойно додані нами індекси:

CREATE NONCLUSTERED INDEX idx_AppointmetnAttendee_AttendeeType
ON [dbo].[AppointmentAttendee] ([UserID],[AttendeeType])

CREATE NONCLUSTERED INDEX [idx_appointment_start] ON [dbo].[Appointment]
(
    [ProjectID] ASC,
    [Start] ASC
)
INCLUDE (   [ID],
    [AllDay],
    [End],
    [Location],
    [Notes],
    [Title],
    [CreatedByID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Повний текст запиту:

https://pastebin.com/Z5szPBfu (LINQ-генерується, я можу / повинен мати можливість оптимізувати вибрані стовпці, але це не має значення для цієї проблеми)


Я щойно помітив, що сканування попередніх планів, які не вичерпалися, було на іншому столі, приблизно однакового розміру. Призначення: 11931 ряд, ЗустрічУчасник: 11937 рядків.
Ім'я професійного звучання

Відповіді:


16

Я відповім на ваші запитання в іншому порядку, ніж ви їх задавали.

4. Це симптом більшої проблеми?

Новий оцінювач потужність в SQL Server 2016 може бути причиною проблеми. SQL Server 2012 використовує застарілий CE, і ви не відчували проблеми з цією версією. Новий оцінювач кардинальності робить різні припущення щодо ваших даних і може генерувати різні плани запитів для одного і того ж SQL. Ви можете мати кращу ефективність для деяких запитів із застарілим CE залежно від вашого запиту та ваших даних. Отже, деякі частини вашої моделі даних можуть не найкращим чином відповідати новим CE. Це нормально, але, можливо, вам доведеться наразі обійтись навколо нового СЕ.

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

Я викину приклад сценарію, який може призвести до такої поведінки. Ти сказав:

Деякі користувачі можуть мати 1 запис дозволів, деякі - до 20 к.

Припустимо, ви збираєте статистику на всіх таблицях, яка видаляє всі плани запитів. Залежно від факторів, згаданих вище, якщо перший запит дня стосується користувача, який має лише 1 запис дозволів, то SQL Server може кешувати план, який добре працює для користувачів з 1 записом, але працює дуже жахливо з користувачами із записами 20k. Якщо перший запит дня стосується користувача, який має записи в 20 тис., То ви можете отримати хороший план для записів на 20 тис. Коли код запускається проти користувача з 1 записом, це може бути не самим оптимальним запитом, але він все одно може закінчитися в мс. Це дійсно звучить як нюхання параметрів. Це пояснює, чому ви не завжди бачите проблему або чому іноді потрібні години, щоб з'явитись.

1. Чи допоможе новий індекс виправити проблему, змусивши його ніколи не вибирати поганий план?

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

поганий план запитів

SQL Server оцінює, що лише один рядок буде повернуто з об'єднання в [Permission]і [Project]. Для кожного ряду на зовнішньому вході буде виконано кластерне сканування індексу[Appointment] . Усі рядки будуть скановані з цієї таблиці, але лише ті, що відповідають фільтруванню [Start], повернуться до оператора приєднання. У оператора з'єднання результати ще більше знижуються.

Описаний вище план запитів може бути нормальним, якщо дійсно є лише один рядок, надісланий на зовнішній вхід з'єднання. Однак якщо оцінка кардинальності від приєднання помилкова, і ми отримаємо, скажімо, 1000 рядків, то SQL Server зробить 1000 кластерних сканерів індексу на[Appointment] . Виконання плану запитів дуже чутливе до питань оцінки.

Найбільш прямим способом ніколи не отримати цей план запитів було б створити індекс покриття проти [Appointment]таблиці. Щось на зразок індексу на [ProjectId]і [Start]має робити це. Схоже, це саме той [idx_appointment_start]індекс, який ви створили для вирішення проблеми. Ще один спосіб відмовити SQL-сервер від вибору плану запитів - це виправити оцінку кардинальності від приєднання на [Permission]і [Project]. Типові способи цього включають зміни коду, оновлення статистики, використання застарілого СЕ, створення статистики багато стовпців, надання SQL Server додаткової інформації про локальні змінні, такі як з RECOMPILEпідказкою, або матеріалізація цих рядків у темпну таблицю. Багато з цих методів не є хорошим підходом, коли вам потрібно час відповіді на рівні мс або потрібно писати код через ORM.

Індекс, який ви створили [AppointmentAttendee], не є прямим способом вирішення проблеми. Однак ви отримаєте статистику на кілька стовпців щодо індексу, і ці статистичні дані можуть перешкодити поганому плану запитів. Індекс може запропонувати більш ефективний спосіб отримати доступ до даних, що також може перешкодити поганому плану запитів, але я не думаю, що існує якась гарантія того, що він не повториться лише при включеному індексі [AppointmentAttendee].

3. Як зробити так, щоб це не трапилося з іншим запитом / планом?

Я розумію, чому ви задаєте це питання, але це надзвичайно широке питання. Моя єдина порада - спробувати краще зрозуміти першопричину нестабільності плану запитів, перевірити наявність правильних індексів для вашого робочого навантаження та ретельно перевірити та контролювати навантаження. У Microsoft є кілька загальних порад, як боротися з регресіями плану запитів, викликаними новим СЕ в SQL Server 2016:

Рекомендований робочий процес для оновлення процесора запитів до останньої версії коду:

  1. Оновіть базу даних до SQL Server 2016, не змінюючи рівень сумісності баз даних (тримайте її на попередньому рівні)

  2. Увімкніть сховище запитів у базі даних. Для отримання додаткової інформації про ввімкнення та використання магазину запитів див. Моніторинг продуктивності за допомогою магазину запитів.

  3. Зачекайте достатній час для збору репрезентативних даних про навантаження.

  4. Змініть рівень сумісності бази даних на 130

  5. Використовуючи SQL Server Management Studio, оцініть, чи є регресії продуктивності для конкретних запитів після зміни рівня сумісності

  6. У випадках, коли є регресії, примушуйте попередній план у магазині запитів.

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

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

2. Чи варто «змушувати» план, який зараз добре працює?

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

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