Przedmowa
Istnieje system informacyjny, którym administruję. System składa się z następujących elementów:
1. Baza danych MS SQL Server
2. Aplikacja serwerowa
3. Aplikacje klienckie
Te systemy informacyjne są zainstalowane na kilku obiektach. Z systemu informatycznego korzysta aktywnie 24 godziny na dobę od 2 do 20 użytkowników jednocześnie na każdym obiekcie. Dlatego nie można wykonywać wszystkich rutynowych czynności konserwacyjnych na raz. Dlatego muszę „rozkładać” defragmentację indeksów SQL Server w ciągu dnia, zamiast defragmentować wszystkie niezbędne pofragmentowane indeksy za jednym pociągnięciem. Dotyczy to również innych operacji.
Właściwość automatycznej aktualizacji statystyk jest ustawiana we właściwościach bazy danych. Poza tym statystyki są aktualizowane na zdefragmentowanym indeksie.
Problem
Mniej więcej rok temu napotkałem następujący problem:
Od czasu do czasu wszystkie zapytania działały wolno. Warto zauważyć, że czas opóźnienia był losowy. Zdarzyło się to na każdym przedmiocie w przypadkowy dzień. Co więcej, gdy zacząłem analizować, jak często występują opóźnienia (za pomocą profilera), dowiedziałem się, że pojawiają się one codziennie w losowym czasie. Użytkownicy po prostu nie zawsze zwracają na nie uwagę, ale traktują je jako jedyne losowe opóźnienie, a potem system znów działa szybko.
Rozwiązywanie problemu
Sprawdziłem wszystkie wolno działające zapytania. Najdziwniejsze było to, że wszystkie zapytania działały wolno w losowym czasie, nawet te najprostsze, takie jak pobranie ostatniego rekordu z tabeli zawierającej kilka tysięcy wierszy.
Następnie wykonałem następujące kroki:
1. Przeanalizowałem logi MS SQL Server i Windows Server, ale nie mogłem znaleźć przyczyny opóźnień.
2. Przeanalizowałem indeksy (fragmentacja itp.), uzupełniłem brakujące i usunąłem nieużywane.
3. Przeanalizowałem zapytania – część zapytań została poprawiona.
4. Przeanalizowałem zadania w SQL Agent i nie mogłem powiązać zadań z problemem opóźnienia.
5. Przeanalizowałem zadania w Harmonogramie zadań i nie mogłem powiązać zadań z problemem opóźnienia.
6. Profiler pokazał wyniki, ale nie przyczynę opóźnień.
7. Sprawdziłem, czy nie ma zakleszczeń – nie ujawniono żadnych długich blokad.
W rezultacie spędziłem ponad 3 miesiące na nieudanych poszukiwaniach przyczyny sporadycznych, wolno działających zapytań. Zdradziłem jednak ciekawostkę — zamiast wskaźnika wykonania Worker dla wszystkich zapytań wzrósł wskaźnik oczekiwania Elapsed. Ten fakt dał mi do zrozumienia, że coś jest nie tak z dyskami. Sprawdziłem je – wszystko było w porządku.
Rozwiązanie
Ku mojemu zdziwieniu przypadkowo ujawniłem, że gdy zapytanie w aplikacji było wykonywane powoli, to w SSMS działało szybko. Jeden artykuł pomógł w rozwiązaniu problemu (przynajmniej sugerował pomysł).
Akapit z artykułu:
W praktyce najważniejszą opcją SET jest ARITHABORT, ponieważ domyślna wartość tej opcji jest inna dla aplikacji i dla SQL Server Management Studio. To wyjaśnia, dlaczego możesz wykryć wolno działające zapytanie w swojej aplikacji, a następnie uzyskać dobrą szybkość, wykonując je w programie SSMS. Aplikacja korzysta z planu, który został zbudowany dla zestawu wartości różniących się od rzeczywistych poprawnych wartości. Podczas gdy uruchamiasz zapytanie w SSMS, najprawdopodobniej pamięć podręczna nie ma jeszcze planu wykonania dla ARITHABORT ON i dlatego SQL Server utworzy plan dla twoich bieżących wartości.
Różnica w wykonaniu wynikała z parametru SET ARITHABORT. Dla wszystkich zapytań wykonywanych w SSMS opcja jest włączona, a dla zapytań z zewnątrz (z aplikacji) – wyłączona. Nie można go włączyć nawet za pomocą prostego zapytania o aplikacje:
SET ARITHABORT ON;
Pojawił się szalony pomysł – czyszczenie pamięci podręcznej proceduralnej w momencie rozłączenia.
Aby przeprowadzić późniejszą kontrolę ręczną, przed zapytaniem w SSMS muszę napisać następującą instrukcję:
SET ARITHABORT OFF;
W ten sposób przeprowadzimy symulację działania aplikacji. Gdy zapytanie trwało przez długi czas, wyczyściłem proceduralną pamięć podręczną. A to zawsze pomagało. Przed wyczyszczeniem proceduralnej pamięci podręcznej zapytanie mogło trwać do 20-30 sekund, a później – 0 sekund.
Następnie przeprowadziłem kolejny eksperyment – czyszczenie całej proceduralnej pamięci podręcznej dla całej bazy danych co godzinę za pomocą agenta SQL:
--cleaning the cache by database id DBCC FLUSHPROCINDB (@db_id);
Następnie wszystkie zapytania działały bardzo szybko (mniej niż 0,05 sekundy). Wystąpiły tylko niektóre przypadki wykonania do 5-10 sekund, ale użytkownicy nie zauważyli żadnych zawieszeń. Co więcej, aktualizacja statystyk nie poprawiła wyników, więc wyłączyłem aktualizację statystyk.
Po kilku kolejnych miesiącach badań odkryłem, że sporadyczne zawieszania się zdarzają się, gdy albo pamięć podręczna zużywa wszystko na serwerze, i nie ma już wolnego miejsca, albo jest wolna pamięć, ale mniej niż 1 GB pamięci RAM lub usługa MS SQL Server zajmuje całą przydzieloną pamięć RAM (poprzez Menedżera zadań). Ale drugie zdarzenie miało miejsce tylko dwa razy w całym badaniu.
Faktem jest, że dosłownie wszystko jest zapisywane w pamięci podręcznej, podczas gdy pamięć podręczna nie zawsze jest uwalniana na czas. Problem z pamięcią podręczną został rozwiązany za pomocą programu EmptyStandbyList.exe.
Skonfigurowałem tę aplikację za pomocą Harmonogramu zadań, aby uruchamiała się 1 raz na godzinę. Po zakończeniu wszystkich prac, od ponad pół roku nie ma zawieszeń zapytań dotyczących wszystkich obiektów.
Jedyne, co pozostaje niejasne, to rzadkie przypadki, gdy jedno zapytanie zawiesza się na 5-10 sekund raz w miesiącu w losowy dzień i o losowej porze. Były 4 takie przypadki i tylko na dwóch obiektach przez pół roku, gdy usługa MS SQL Server na krótki czas zajmuje całą przydzieloną pamięć.
Zasadniczo nie ma potrzeby kopania głębiej, ponieważ użytkownicy nie zauważają żadnych zawieszeń i wszystko działa dobrze, ale jeśli ktoś ma jakieś myśli, będę wdzięczny za udostępnienie.
Ten artykuł został napisany, aby pomóc tym, którzy napotykają takie problemy, ponieważ nie znalazłem wyczerpującej odpowiedzi w Internecie i spędziłem dużo czasu na badaniu problemu i znalezieniu rozwiązania.
Zobacz też:
- Implementowanie wskaźnika wydajności serwera SQL dla zapytań, procedur przechowywanych i wyzwalaczy
- Automatyzacja defragmentacji indeksu w bazie danych MS SQL Server
Przydatne narzędzie:
dbForge Query Builder dla SQL Server – pozwala użytkownikom szybko i łatwo tworzyć złożone zapytania SQL za pomocą intuicyjnego interfejsu wizualnego bez ręcznego pisania kodu.