Висловлювання SQL Server періодично повільно на SQL Server 2008 R2


13

У одного з наших клієнтів у нас виникли деякі проблеми з ефективністю нашого додатку. Це веб-додаток .NET 3.5, який споживає та оновлює дані в базі даних SQL Server. В даний час наше виробниче середовище складається з машини Windows 2008 R2 як передньої частини та кластера SQL Server 2008 R2 на задньому. Наш додаток використовує COM + та MSDTC для підключення до бази даних.

Ось що відбувається: наші кінцеві користувачі іноді скаржаться на повільність у програмі. Для завантаження деяких сторінок потрібно трохи більше часу, ніж очікувалося. Намагаючись з’ясувати, що відбувається, мені вдалося з’ясувати якусь дивну поведінку на стороні бази даних, яка може бути причиною погіршення продуктивності. Я помітив, що іноді є деякі заяви SQL, які потребують набагато більше часу для виконання того, що можна було б очікувати. Мені вдалося ідентифікувати деякі з цих висловлювань (в основному це виклики деяких збережених процедур нашого додатку), використовуючи трафік профілю (з шаблоном TSQL_Duration) для ідентифікації тривалих запитів.

Проблема полягає в тому, що коли я запускаю ці збережені процедури безпосередньо в базі даних на SQL Management Studio, іноді вони займають багато часу (приблизно 7/8 секунд), в іншому випадку вони швидкі (менше 1 секунди). Я не знаю, чому це відбувається, і це зводить мене з глузду, тому що SQL-машина (4 ядра, 32 ГБ) не використовується жодними іншими програмами, і ці запити не повинні зайняти це багато часу.

Не будучи DBA або гуру SQL Server, я намагався розглянути деякі речі, які можуть допомогти мені зрозуміти проблему. Ось кроки, які я вжив, щоб спробувати розібратися в проблемі і що я дізнався до цих пір:

  • Весь код TSQL, викликаний програмою, записується в збережені процедури.
  • Я визначив деякі тривалі запити у профіле SQL Server, однак, коли я запускаю їх у програмі Management Studio, вони запускають багато часу (від 4 до 10 сек.), Або швидко виконують (менше 1 сек.). Я запускаю абсолютно ті самі запити з тими ж даними, що передаються в параметрах. Ці запити в основному зберігаються в процедурах із виділеними операторами.
  • Я спробував переглянути статистику очікувань та черг, щоб спробувати визначити, чи є процеси, які чекають на деяких ресурсах. Я запустив наступний запит:

WITH Waits AS
    (SELECT
        wait_type,
        wait_time_ms / 1000.0 AS WaitS,
        (wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
        signal_wait_time_ms / 1000.0 AS SignalS,
        waiting_tasks_count AS WaitCount,
        100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
        ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN (
        'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
        'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
        'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',  'BROKER_TO_FLUSH',
        'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT',     'DISPATCHER_QUEUE_SEMAPHORE',
        'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
        'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
        'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
        'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
        'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
    )
SELECT
    W1.wait_type AS WaitType, 
    CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
    CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
    CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
    W1.WaitCount AS WaitCount,
    CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
    CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
    CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
    CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
    INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount,    W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO

Ось що я дізнався:

  • Після того як я скинув статистику за допомогою DBCC SQLPERF (приблизно через 1 або 2 години після), типи очікування, які у мене найбільше, - це SOS_SCHEDULER_YIELD і WRITELOG
  • З часом (після приблизно 1-денного виконання) типи очікування, які найбільше трапляються в базі даних, - це CXPACKET (67%) і OLEDB (17%), хоча середній час очікування для кожного не є довгим. Я також зауважив, що довші запущені оператори, визначені в SQL Profiler, - це виклики до збережених процедур, які повертають більше одного набору результатів (часто 3). Чи може тут виникнути проблема паралелізму? Чи я можу спробувати визначити, чи це причина проблеми?
  • Я десь читав, що очікування OLEDB може бути викликано дзвінками до ресурсів OLEDB, як-от пов'язані сервери. У нас є зв'язаний сервер для з'єднання з машиною служб індексування (MSIDXS), однак жодна з заяв, ідентифікованих як давно запущена, не використовує цей пов'язаний сервер.
  • Вищий середній час очікування, який я маю, - це очікування типу LCK_M_X (середнє приблизно 1,5 секунди), але такі типи очікування трапляються не дуже часто порівняно з іншими типами (наприклад, 64 LCK_M_X очікує проти 10 823 CXPACKET очікує за той самий проміжок часу ).
  • Я помітив одне, що служба MSDTC не кластеризована. Служба SQL Server кластеризована, але не MSDTC. Чи може через це бути хіт вистави? Ми використовуємо MSDTC, оскільки наш додаток використовує Enterprise Services (DCOM) для доступу до бази даних, але сервери були встановлені не та не налаштовані нами, а нашим клієнтом.

Хтось може допомогти мені зрозуміти ці дані? Чи може хтось подати мені руку на розуміння того, що може статися? Чи можу я щось зробити на сервері, щоб спробувати розібратися? Чи варто поговорити з командою з розробки додатків?

Відповіді:


4

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

WRITELOG - дуже поширений тип очікування, тому не турбуйтеся про це. Дивлячись на SOS_SCHEDULER_YIELD вказують тиск на процесор, а також CXPACKET, можливо, мають бути деякі відсутні індекси, і ви можете отримувати багато даних із запитів для системи OLTP. Я пропоную вам переглянути пропущені індекси DMV і побачити, чи є якісь індекси (майже впевнені, що їх буде більше), які є в сумнівних документах.

http://sqlfool.com/2009/04/a-look-at-missing-indexes/

http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-missing-indexes-on-a-sql-server-2008-or-2005-instance-along-with-the- create-index-команди /

Шукайте публікацію Джонатана Кехаяса на sqlblog.com і про це.

Також погляньте на нюхання параметрів.

http://sommarskog.se/query-plan-mysteries.html

http://pratchev.blogspot.com/2007/08/parameter-sniffing.html

Це НЕ конкурентна відповідь на ваші потреби, а хороша відправна точка. Повідомте нас, якщо вам потрібно більше деталей.


1

У нас була подібна проблема після того, як один із співробітників переписав кілька збережених процедур. Виявилося, що було надмірне розгалуження та будувався Dynamic SQL , який суттєво змінив пункт де.

Наприклад (спрощено звичайно):

Якщо модель була "X", то пункт де шукав ProductCode рівними певними значеннями.
Якщо модель була "Y", то пункт де шукав ProductType рівним певним значенням.

SQL Server створить план запитів на основі вхідних параметрів при першому виконанні збереженої процедури. Отже, якщо план запитів побудований на логіці, яка використовує "ProductCode", і ви запитуєте "ProductType" дорівнює, це невідповідний план запитів і, швидше за все, призводить до повного сканування таблиці.

Ви можете спробувати розмістити " З РЕКОМПЛІЮ " у верхній частині збереженої процедури. СТВОРИТИ ПРОЦЕДУРА (Transact-SQL)

Найкраще я можу описати це так:

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


Використання exec()функції пояснить спостережувану поведінку. У цьому випадку використання, sp_executesqlяк правило, вирішує проблеми з динамічними операторами SQL.
ajeh

1

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

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

Яка орієнтовна кількість рядків у плані виконання порівняно з кількістю фактичних рядків повернуто?

Чи відповідає компільований параметр фактичному параметру запиту?

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

Вибір плану виконання тісно пов'язаний зі статистикою SQL, тому це гарна ідея регулярно відновлювати статистику.

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

Якщо відновлення статистики не вирішує проблему, ви можете запустити найдорожчий оператор (и) у збереженій процедурі OPTION (RECOMPILE)


0

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

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