Instrukcje SQL Server sporadycznie spowalniają SQL Server 2008 R2

13

W przypadku jednego z naszych klientów mieliśmy problemy z wydajnością naszej aplikacji. Jest to aplikacja internetowa .NET 3.5, która zużywa i aktualizuje dane w bazie danych SQL Server. Obecnie nasze środowisko produkcyjne składa się z komputera z systemem Windows 2008 R2 jako interfejsu użytkownika i klastra SQL Server 2008 R2 z zaplecza. Nasza aplikacja używa COM + i MSDTC do łączenia się z bazą danych.

Oto, co się dzieje: nasi użytkownicy końcowi czasami narzekają na powolność działania aplikacji. Niektóre strony ładują się dłużej niż można by się spodziewać. Próbując dowiedzieć się, co się dzieje, udało mi się znaleźć dziwne zachowanie po stronie bazy danych, które może być przyczyną obniżenia wydajności. Zauważyłem, że czasami są pewne instrukcje SQL, których uruchomienie zajmuje dużo więcej czasu, niż można by się spodziewać. Udało mi się zidentyfikować niektóre z tych instrukcji (głównie wywołania niektórych procedur przechowywanych naszej aplikacji) za pomocą śledzenia profilera (z szablonem TSQL_Duration) w celu zidentyfikowania długo działających zapytań.

Problem polega na tym, że kiedy uruchamiam te procedury składowane bezpośrednio w bazie danych w SQL Management Studio, czasem zabierają one dużo czasu (około 7/8 sekund), innym razem są szybkie (poniżej 1 sekundy). Nie wiem, dlaczego tak się dzieje i doprowadza mnie to do szału, ponieważ maszyna SQL (4 rdzenie, 32 GB) nie jest używana przez inne aplikacje, a te zapytania nie powinny trwać tak długo.

Nie będąc guru DBA ani SQL Server, próbowałem przyjrzeć się niektórym rzeczom, które mogą pomóc mi zrozumieć problem. Oto kroki, które podjąłem, aby rozwiązać problem i czego do tej pory się dowiedziałem:

  • Cały kod TSQL wywoływany przez aplikację jest zapisywany w procedurach przechowywanych.
  • Zidentyfikowałem niektóre z długo działających zapytań w programie SQL Server do profilowania, jednak kiedy uruchamiam je w Management Studio, ich uruchomienie trwa długo (od 4 do 10 sekund) lub szybko (poniżej 1 sekundy). Korzystam z tych samych zapytań z tymi samymi danymi przekazanymi w parametrach. Te zapytania są głównie procedurami składowanymi, w których znajdują się instrukcje select.
  • Próbowałem przyjrzeć się statystykom oczekiwania i kolejek, aby dowiedzieć się, czy niektóre zasoby oczekują na procesy. Uruchomiłem następujące zapytanie:

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

Oto, co odkryłem:

  • Po zresetowaniu statystyk za pomocą DBCC SQLPERF (około 1 lub 2 godziny później), typy oczekiwania, które mam najwięcej, to SOS_SCHEDULER_YIELD i WRITELOG
  • Z czasem (po około 1-dniowym wykonaniu) typami oczekiwania, które najczęściej występują w bazie danych, są CXPACKET (67%) i OLEDB (17%), mimo że średni czas oczekiwania dla każdego z nich nie jest długi. Zauważyłem również, że dłużej działające instrukcje zidentyfikowane w SQL Profiler są wywołaniami procedur przechowywanych, które zwracają więcej niż jeden zestaw wyników (często 3). Czy może tu występować problem paralelizmu? Czy jest jakiś sposób, aby spróbować ustalić, czy to jest przyczyną problemu?
  • Czytałem gdzieś, że oczekiwania OLEDB mogą być spowodowane wywołaniami do zasobów OLEDB, takich jak połączone serwery. Mamy połączony serwer do połączenia z maszyną usług indeksujących (MSIDXS), jednak żadna z instrukcji określanych jako długo działające nie korzysta z tego połączonego serwera.
  • Wyższy średni czas oczekiwania mam na oczekiwania typu LCK_M_X (średnio około 1,5 sekundy), ale te typy oczekiwania nie zdarzają się często w porównaniu z innymi typami (na przykład 64 LCK_M_X czeka na 10,823 CXPACKET czeka na ten sam okres czasu ).
  • Zauważyłem tylko, że usługa MSDTC nie jest klastrowana. Usługa SQL Server jest klastrowana, ale nie MSDTC. Czy może być z tego powodu hit wydajności? Korzystamy z MSDTC, ponieważ nasza aplikacja korzysta z usług Enterprise Services (DCOM) w celu uzyskania dostępu do bazy danych, ale serwery nie zostały zainstalowane i skonfigurowane przez nas, ale przez naszego klienta.

Czy ktoś może mi pomóc lepiej zrozumieć te dane? Czy ktoś może mi pomóc zrozumieć, co się dzieje? Czy jest coś, co mogę zrobić na serwerze, aby spróbować to rozgryźć? Czy powinienem porozmawiać z zespołem programistów?

Dori
źródło

Odpowiedzi:

4

Dziękuję za szczegółowe wyjaśnienie problemu (jedno z najlepiej postawionych pytań).

WRITELOG jest bardzo popularnym rodzajem oczekiwania, więc nie przejmuj się. Patrząc na SOS_SCHEDULER_YIELD wskazują na obciążenie procesora, a także na CXPACKET, możliwe, że brakuje niektórych indeksów i możesz pobierać dużo danych z zapytań dla systemu OLTP. Sugeruję, aby spojrzeć na brakujące indeksy DMV i sprawdzić, czy istnieją jakieś indeksy (prawie pewne, że będzie ich więcej niż kilka), które znajdują się w wątpliwych procesach.

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-command /

Poszukaj też postu Jonathana Kehayiasa na sqlblog.com.

Spójrz także na wąchanie parametrów.

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

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

To NIE jest konkurencyjna odpowiedź na twoje potrzeby, ale dobry punkt wyjścia. Daj nam znać, jeśli potrzebujesz więcej szczegółów.

Sankar Reddy
źródło
1

Podobny problem mieliśmy po tym, jak jeden z pracowników przepisał kilka procedur przechowywanych. Okazało się, że powstało nadmierne rozgałęzienie i budowano dynamiczny SQL , który znacznie zmienił klauzulę where.

Na przykład (oczywiście uproszczony):

Jeśli model jest „X” w przypadku gdy klauzula szukał kodu produktu równa pewne wartości.
Jeśli Model był „Y”, klauzula where szukała ProductType równych pewnych wartości.

SQL Server zbuduje plan zapytań na podstawie parametrów wejściowych przy pierwszym uruchomieniu procedury składowanej. Tak więc, jeśli plan zapytań oparty jest na logice, która używa „ProductCode” równa się, a pytasz o „ProductType” równa się, to jest to niedopasowany plan zapytań i najprawdopodobniej powoduje pełne skanowanie tabeli.

Możesz spróbować umieścić „ Z RECOMPILE ” na górze procedury składowanej. UTWÓRZ PROCEDURĘ (Transact-SQL)

Najlepszy sposób, jaki mogę to opisać, jest następujący:

Załóżmy, że masz listę nazwisk i numerów telefonów posortowaną według nazwiska. Działa to doskonale do znajdowania osób przy użyciu ich nazwiska (plan zapytań oparty na nazwisku). Załóżmy teraz, że potrzebujesz wszystkich nazwisk i numerów telefonów w numerze kierunkowym 203. Jeśli lista jest posortowana według nazwiska, jedynym sposobem na uzyskanie pełnej listy wszystkich osób posiadających numer kierunkowy 203 jest rozpoczęcie od góry i przeczytanie kolejno każdego z nich i każdy rekord. (Pełny skan tabeli).

Michael Riley - AKA Gunny
źródło
Użycie exec()funkcji wyjaśniłoby zaobserwowane zachowanie. W takim przypadku użycie sp_executesqlnormalnie rozwiązuje problemy z dynamicznymi instrukcjami SQL.
ajeh
1

Jeśli zapytania działają w SSMS i aplikacji sporadycznie szybko i wolno, może występować problem ze statystykami lub wąchaniem parametrów.

Wykonałbym te procedury składowane, a następnie przejrzałem plan wykonania, aby pobrać właściwości operatora root (zielony węzeł z lewej strony każdej instrukcji).

Jaka jest szacunkowa liczba wierszy w planie wykonania, a ile rzeczywistych wierszy zostało zwróconych?

Czy skompilowany parametr odpowiada faktycznemu parametrowi zapytania?

Jeśli plan wykonania został utworzony dla parametru, który zwraca tylko garść wierszy, i uruchomisz tę samą procedurę z parametrem, który zwraca ogromną liczbę wierszy, SQL może użyć niepoprawnego planu wykonania dla zapytania.

Wybory w planie wykonania są ściśle powiązane ze statystykami SQL, więc dobrym pomysłem jest regularne przebudowywanie statystyk.

Jeśli masz procedurę składowaną, która czasami zwraca małe ilości danych lub ogromne ilości danych w zależności od podanego parametru, możesz mieć problem z wąchaniem parametru.

Jeśli odbudowanie statystyk nie rozwiąże problemu, możesz uruchomić najdroższe instrukcje w procedurze przechowywanej za pomocą OPTION (RECOMPILE)

Andre Ranieri
źródło
0

Po zidentyfikowaniu długo działających zapytań możesz pobrać plany wykonania tych procedur z pamięci podręcznej i sprawdzić, czy możesz tam określić problem. Często występują niejawne lub w czasie wykonywania konwersje typów danych. Ponadto, jeśli wyczyścisz lub wstawisz dużo danych, wskazane jest również zaktualizowanie statystyk.

Chandan Jha
źródło