Операторы 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 ГБ) не используется никакими другими приложениями, и эти запросы не должны запускаться так долго.

Не будучи администратором базы данных или гуру 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- создание-индекс-команды /

Посмотрите на сообщение Джонатана Кехайяса на sqlblog.com об этом тоже.

Кроме того, взгляните на параметр нюхает.

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

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

Это НЕ исчерпывающий ответ для ваших нужд, а хорошая отправная точка. Дайте нам знать, если вам нужно больше деталей.

Санкар Редди
источник
1

У нас была похожая проблема после того, как один из сотрудников переписал несколько хранимых процедур. Оказалось, что было чрезмерное ветвление и строился динамический SQL , что значительно изменило предложение where.

Например (упрощенно конечно):

Если Model был «X», то в предложении where код ProductCode соответствовал определенным значениям.
Если в качестве модели указано «Y», то в предложении where для ProductType используется значение, равное некоторым

SQL Server создаст план запроса на основе входных параметров при первом выполнении хранимой процедуры. Таким образом, если план запроса основан на логике, которая использует «ProductCode», равный, а вы запрашиваете «ProductType», равный, это несоответствующий план запроса и, скорее всего, приводит к полному сканированию таблицы.

Вы можете попробовать поместить « WITH RECOMPILE » вверху хранимой процедуры. СОЗДАНИЕ ПРОЦЕДУРЫ (Transact-SQL)

Лучший способ описать это следующим образом:

Предположим, у вас есть список имен и телефонных номеров, отсортированных по фамилии. Это отлично подходит для поиска людей по фамилии (план запроса на основе фамилии). Теперь предположим, что вам нужны все имена и номера телефонов в телефонном коде 203. Если ваш список отсортирован по фамилии, единственный способ получить полный список всех людей с телефонным кодом 203 - это начать сверху и последовательно читать через каждый и каждая запись. (Полное сканирование таблицы).

Майкл Райли - AKA Gunny
источник
Использование exec()функции объясняет наблюдаемое поведение. В этом случае использование sp_executesqlобычно разрешает проблемы с динамическими операторами SQL.
ajeh
1

Если запросы в SSMS и в приложении периодически выполняются быстро и медленно, возможно, у вас возникла проблема с анализом статистики или параметров.

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

Каково предполагаемое количество строк в плане выполнения по сравнению с тем, сколько фактических строк было возвращено?

Соответствует ли скомпилированный параметр фактическому параметру запроса?

Если план выполнения был создан для параметра, который возвращает только несколько строк, и вы выполняете ту же процедуру с параметром, который возвращает огромное количество строк, SQL может использовать неверный план выполнения для запроса.

Выбор плана выполнения тесно связан со статистикой SQL, поэтому рекомендуется регулярно перестраивать статистику.

Если у вас есть хранимая процедура, которая иногда возвращает небольшие объемы данных или огромные объемы данных в зависимости от предоставленного параметра, у вас может возникнуть проблема с анализом параметров.

Если восстановление вашей статистики не решает проблему, вы можете выполнить самые дорогие операторы в хранимой процедуре с помощью OPTION (RECOMPILE)

Андре Раньери
источник
0

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

Чандан Джа
источник