Це симптом перевантаженого сервера?


12

Я намагався діагностувати уповільнення у програмі. Для цього я зареєстрував розширені події SQL Server .

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

Часи виконання збереженої процедури значно відрізняються. Багато виконань цієї збереженої процедури повертається в <1s:

введіть тут опис зображення

А для цього «швидкого» відра - це набагато менше 1 с. Це насправді близько 90 мс:

введіть тут опис зображення

Але є довгий хвіст користувачів, яким доводиться чекати 2, 3, 4 секунди. Деяким доводиться чекати 12s, 13s, 14s. Тоді є справді бідні душі, яким доводиться чекати 22, 23, 24 години.

А після 30-х років клієнтська програма здається, перериває запит, і користувачеві довелося чекати 30 секунд .

Кореляція для пошуку причинного зв'язку

Тому я спробував співвіднести:

  • тривалість та логічне зчитування
  • тривалість та фізичні показання
  • тривалість vs час процесора

І нібито жодна кореляція не дає; ніхто, здається, не є причиною

  • тривалість порівняно з логічним читанням : чи мало, чи багато логічного читання, тривалість все ще сильно коливається :

    введіть тут опис зображення

  • тривалість порівняно з фізичними читаннями : навіть якщо запит не надходив з кеша, і було потрібно багато фізичних читань, це не впливає на тривалість:

    введіть тут опис зображення

  • тривалість порівняно з процесорним часом : Незалежно від того, чи запит зайняв 0 с CPU, або 2,5 секунди часу процесора, тривалість має однакову мінливість:

    введіть тут опис зображення

Бонус : я помітив, що тривалість v «Фізичні читання» та « Тривалість проти часу процесора» виглядають дуже схоже. Це доведено, якщо я спробую співвіднести час процесора з фізичним читанням:

введіть тут опис зображення

Виявляється, багато використання процесора відбувається від вводу / виводу. Хто знав!

Отже, якщо в акті виконання запиту немає нічого, що може враховувати відмінності у часі виконання, чи означає це, що це щось не пов'язане з процесором або жорстким диском?

Якщо процесор або жорсткий диск були вузьким місцем; хіба це не було вузьким місцем?

Якщо ми припускаємо гіпотезу, що саме вузол був саме процесором; що ЦП не працює на цьому сервері:

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

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

  • то чи не будуть тривати страти за допомогою більш фізичного читання довше?
  • оскільки вони мають комплектуватися з іншими, використовуючи перевантажений жорсткий диск вводу / виводу?

Сама збережена процедура ні записує, ні вимагає.

  • Зазвичай він повертає 0 рядків (90%).
  • Іноді він поверне 1 ряд (7%).
  • Рідко він поверне 2 ряди (1,4%).
  • І в гірших випадках він повернув понад 2 ряди (один раз повертаючи 12 рядків)

Тож це не так, як це повернення шаленого обсягу даних.

Використання серверного процесора

Використання процесора на сервері в середньому становить близько 1,8%, з випадковим стрибком до 18% - тому, здається, не так це завантаження процесора:

введіть тут опис зображення

Тому процесор сервера не здається перевантаженим.

Але сервер є віртуальним ...

Щось поза Всесвітом?

Єдине, що я можу собі уявити, - це те, що існує поза всесвіту сервера.

  • якщо це не логічно читає
  • і це не фізичні читання
  • і це не використання процесора
  • і це не завантаження процесора

І це не так, як це параметри збереженої процедури (адже видача одного і того ж запиту вручну і не займає 27 секунд - це займає ~ 0 секунд).

Що ще може враховувати, що сервер іноді займає 30 секунд, а не 0 секунд, щоб запустити ту ж компільовану збережену процедуру.

  • пункти пропуску?

Це віртуальний сервер

  • господар перевантажений?
  • інший VM на тому ж хості?

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

  • 2 секунди
  • 1 секунда
  • 30 секунд
  • 3 секунди
  • 2 секунди

І немає інших особливо напружених предметів, які я можу знайти. Це не під час резервного копіювання журналу кожні 2 години.

Що ще могло бути?

Чи я можу сказати окрім: "сервер" ?

Редагувати : співвідносити час доби

Я зрозумів, що я співвідносив тривалість з усім:

  • логічні читання
  • фізичні читання
  • використання процесора

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

Або , можливо, уповільнення цього відбувається в патронах під час контрольно - пропускних пунктів?

Ні:

введіть тут опис зображення

Чотириядерний Intel Xeon Gold 6142.

Редагувати - люди гіпотизують план виконання запитів

Люди гіпотезують, що плани виконання запитів повинні бути різними між "швидким" та "повільним". Вони не є.

І це ми можемо побачити одразу з перевірки.

Ми знаємо, що більша тривалість питання не через "поганий" план виконання:

  • той, який брав більше логічних читань
  • той, який споживає більше процесора за рахунок більшої кількості приєднань та ключових пошуків

Тому що якби збільшення читання чи збільшення процесора було причиною збільшення тривалості запитів, ми б це вже бачили вище. Кореляції немає.

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

введіть тут опис зображення

Кореляції стає ще менше - що є парадоксальним.


Редагувати : оновлено діаграми розкидання, щоб вирішити помилку в графіках розкидання Excel з великою кількістю значень.

Наступні кроки

Наступні мої кроки полягають у тому, щоб змусити когось із серверів генерувати події для заблокованих запитів - через 5 секунд:

EXEC sp_configure 'blocked process threshold', '5';
RECONFIGURE

Це не пояснить, чи запити заблоковані протягом 4 секунд. Але, можливо, все, що блокує запит протягом 5 секунд, також блокує деякі протягом 4 секунд.

Повільні плани

Ось повільний план двох збережених процедур, що виконуються:

  • `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'
  • `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'

Ця ж збережена процедура з тими ж параметрами переходить назад до спини:

| Duration (us) | CPU time (us) | Logical reads | Physical reads | 
|---------------|---------------|---------------|----------------|
|    13,984,446 |        47,000 |         5,110 |            771 |
|     4,603,566 |        47,000 |         5,126 |            740 |

Дзвінок 1:

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

Дзвінок 2

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

Є сенс, щоб плани були однаковими; він виконує ту саму збережену процедуру, з тими ж параметрами.


2
Чи можете ви опублікувати плани запитів - хороший та проти поганий пробіг?
Кін Шах

4
Моя перша здогадка звідси буде блокувати ...
Tibor Karaszi

3
Хто спростував це? Це дуже детальне, всебічно вивчене питання, хоча йому не вистачає планів запитів! +1 від мене!
Vérace

4
Як ви дійшли висновку, що плани запитів "однакові"? Ви просто маєте на увазі, що вони мають однакову форму? Опублікуйте їх десь, щоб ми також могли порівняти. Просто сказати нам, що вони однакові, не означає, що вони ідентичні.
Аарон Бертран

3
Додавання акустичних планів виконання за допомогою PasteThePlan може дати нам уявлення про те, на що чекає запит.
Ранді Вертонген

Відповіді:


2

Погляньте на wait_stats, і він покаже, які найбільші вузькі місця є на вашому SQL-сервері.

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

Моніторинг продуктивності взагалі нічого не турбує із кешами SQL або використанням оперативної пам’яті та IO на сервері.

Що допомогло звузити розслідування, це запит статистики очікування, яку збирає SQL в sys.dm_os_wait_stats

Прекрасний сценарій на веб-сайті SQLSkills покаже вам ті, з якими ви переживаєте найбільше. Потім ви можете звузити пошук, щоб виявити причини.

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

SELECT OSW.session_id,
       OSW.wait_duration_ms,
       OSW.wait_type,
       DB_NAME(EXR.database_id) AS DatabaseName
FROM sys.dm_os_waiting_tasks OSW
INNER JOIN sys.dm_exec_sessions EXS ON OSW.session_id = EXS.session_id
INNER JOIN sys.dm_exec_requests EXR ON EXR.session_id = OSW.session_id
OPTION(Recompile);

Вищенаведений запит та додаткові деталі розміщені на веб-сайті MSSQLTips .

sp_BlitzFirstСценарій від Brent Ozar в сайті також покаже вам , що викликає уповільнення.

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