Skoki CPU o stałych porach w SQL: jak znaleźć zapytania, które psują cache
Jak diagnozować skoki CPU w stałych porach w SQL Server: korelacja metryk z jobami i zapytaniami, recompilations, parameter sniffing, statystyki, antywzorce oraz bezpieczne działania naprawcze i monitoring.
Dlaczego skoki CPU w stałych porach często wynikają z cache i planów zapytań, a nie z „większego ruchu”?
Skoki CPU pojawiające się zawsze o tych samych godzinach częściej wskazują na zdarzenia cykliczne wewnątrz silnika SQL niż na nagły wzrost liczby użytkowników. „Większy ruch” zwykle daje bardziej rozproszony w czasie wzrost obciążenia (zależny od zachowania klientów), natomiast powtarzalny pik co 5/15/60 minut albo o stałej godzinie jest typowy dla mechanizmów, które okresowo zmieniają warunki wykonywania zapytań: zawartość cache i dobór planów.
Cache wpływa na to, czy zapytanie pracuje na „ciepłych” danych i planach, czy musi od nowa budować kontekst wykonania i ponownie wczytywać potrzebne strony do pamięci. Gdy cache jest opróżniany lub intensywnie wypierany (np. przez inne zapytania, duże operacje lub wydarzenia serwisowe), kolejne uruchomienia tych samych zapytań nagle przechodzą z szybkich, przewidywalnych przebiegów na kosztowne obliczeniowo: pojawia się więcej pracy CPU na etapie kompilacji/ponownej kompilacji oraz więcej przetwarzania wynikającego z gorszej lokalności danych i dodatkowych operacji po stronie procesora.
Plany zapytań dodają drugi, często kluczowy mechanizm: nawet przy podobnym „ruchu” to samo zapytanie może w różnych momentach dostać inny plan wykonania. Jeśli plan zostaje unieważniony, wyrzucony z cache lub wymuszona jest rekompilacja, silnik musi ponownie dobrać plan i może wybrać wariant znacząco bardziej CPU‑żerny (np. inny typ złączeń, inne oszacowania kardynalności, inna kolejność operacji). Ponieważ takie zdarzenia (inwalidacje, rotacja cache, okresowe zadania utrzymaniowe, aktualizacje statystyk) mają charakter cykliczny, ich efekt bywa widoczny jako regularne piki CPU mimo braku realnej zmiany liczby żądań.
Jakie źródła danych i metryki sprawdzić najpierw, żeby skorelować skok CPU z konkretnymi zapytaniami lub jobami?
Zacznij od osi czasu: potrzebujesz jednego, spójnego „znacznika czasu” skoku CPU oraz źródeł, które pozwalają przypisać temu oknu czasowemu konkretne sesje i ich zapytania. Najpierw potwierdź skok na poziomie hosta (CPU % i load average z metrykami systemowymi), a równolegle sprawdź w samym SQL, czy w tym samym przedziale rośnie CPU time przypisany do zapytań (a nie np. czas oczekiwania na I/O). To rozróżnienie jest kluczowe: korelować chcesz metrykę CPU per zapytanie/job, nie tylko „czas trwania” czy „liczbę wykonań”.
W praktyce najszybciej łączy się trzy warstwy danych: (1) metryki systemowe (CPU), (2) wewnętrzne statystyki/telemetrię silnika SQL dla aktywnych sesji i wykonujących się zapytań (kto w danej chwili zużywa CPU), oraz (3) harmonogram jobów (co dokładnie startuje o tej porze). Jeśli masz monitoring APM/DB, użyj go jako agregatora, ale i tak warto zejść do źródeł wbudowanych w bazę, bo to one dają identyfikatory zapytań, plany i czasy CPU.
Host/OS (punkt odniesienia i okno czasu): CPU utilization (user/system/steal), load average, liczba runnable threads oraz kontekst przełączeń. Te metryki mówią, czy to realny wzrost pracy CPU, czy np. contention lub problem z przydziałem CPU w wirtualizacji (wysokie steal). Zapisz dokładne okno (np. 08:00–08:05), bo będzie użyte do filtrowania danych z bazy.
Silnik SQL – „kto jadł CPU” w tym oknie: metryki per zapytanie/per plan: CPU time, liczba wykonań, średni i całkowity CPU, oraz (jeśli dostępne) czas CPU w podziale na worker threads. Dodatkowo zestaw to z metrykami oczekiwań (waits): jeśli CPU rośnie, a dominują waity I/O lub locki, korelacja z CPU będzie myląca. W tej warstwie kluczowe są identyfikatory zapytania (tekst/SQL ID/query hash) oraz identyfikator planu, żeby móc wskazać konkretny „wariant” wykonania.
Aktywne sesje i wykonywane requesty (migawka z momentu skoku): dane o aktualnie uruchomionych sesjach/procesach, bieżącym zapytaniu, użyciu CPU przez sesję/proces, liczbie workerów oraz typach waitów. To najszybsza droga do mapowania „CPU skoczyło” → „te 2–3 sesje dominowały”. Jeśli skoki są cykliczne, skonfiguruj okresowe snapshoty/trace w tych minutach, bo pojedyncze ręczne spojrzenie często nie trafia w moment piku.
Harmonogram i logi jobów (korelacja z cyklem): historia uruchomień jobów: czas startu/końca, status, oraz (jeśli silnik to udostępnia) krok joba i wykonywany w nim SQL. Szukasz zbieżności czasowej: joby startujące równo o stałej porze i te, które w oknie piku wykazują nietypowo długi czas lub większą liczbę uruchomień (np. retry). To pozwala odróżnić skok generowany przez zapytanie ad-hoc od zaplanowanej automatyki.
Minimalny zestaw, który zwykle wystarcza do pierwszej poprawnej korelacji, to: „CPU % na hoście w oknie X”, „TOP zapytania/plany po łącznym CPU time w oknie X” oraz „joby startujące w oknie X wraz z identyfikatorem kroku/SQL”. Jeśli te trzy widoki czasowe się pokrywają, masz wiarygodną hipotezę, które zapytania lub joby odpowiadają za skok CPU.
Jak znaleźć zapytania, które najczęściej powodują recompilations i destabilizują plan cache?
Najpewniejszą drogą jest korelacja trzech rzeczy: (1) które zapytania kompilują się najczęściej, (2) które mają w cache wiele wersji planu, oraz (3) czy przy ich wykonaniu rośnie liczba kompilacji/rekompilacji w licznikach instancji. W praktyce zaczyna się od DMV, bo pozwalają wskazać konkretne query_hash/sql_handle, a dopiero potem przechodzi do szczegółów tekstu i planu.
Do identyfikacji zapytań z wysoką częstością kompilacji użyj widoku sys.dm_exec_query_stats, który przechowuje statystyki per plan. Najszybciej wyłapiesz problem, sumując liczbę wykonań po query_hash i zestawiając ją z liczbą odrębnych planów dla tego samego zapytania. Zapytania destabilizujące cache typowo mają relatywnie dużo różnych planów w stosunku do liczby wykonań (wysoki „plan churn”), co często wynika z braku parametryzacji, wymuszania kompilacji albo wahań parametrów generujących różne plany.
Następnie sprawdź, czy plan cache zawiera wiele wpisów dla tego samego tekstu/zapytania. Do tego służą sys.dm_exec_cached_plans (ile planów i jakiego typu) oraz powiązanie z sys.dm_exec_sql_text i ewentualnie sys.dm_exec_query_plan, żeby zidentyfikować konkretną procedurę/ad-hoc i potwierdzić, że to nie są różne zapytania o podobnym kształcie. W samym cache szukasz szczególnie wielu planów typu Adhoc lub wielu planów dla jednej procedury z różnymi plan_handle.
Żeby odróżnić „dużo planów w cache” od rzeczywistych rekompilacji w czasie, zepnij to z licznikami wydajności. W momencie skoku CPU sprawdź SQLServer:SQL Statistics dla SQL Compilations/sec i SQL Re-Compilations/sec; jeśli rosną razem ze skokiem CPU, masz silny sygnał, że CPU zużywa kompilator, a nie samo wykonywanie planów. Jeśli licznik rekompilacji jest wysoki, zawęź dochodzenie do obiektów i zdarzeń, które do nich prowadzą.
Na koniec, aby wskazać konkretne zapytania wywołujące rekompilacje „tu i teraz”, użyj Extended Events. Najbardziej użyteczne są zdarzenia sql_statement_recompile (dla statement-level) oraz sp_cache_miss/sql_batch_completed (w zależności od scenariusza) wraz z akcjami takimi jak sql_text, query_hash, database_id, object_id i plan_handle. To pozwala połączyć moment rekompilacji z konkretnym tekstem, obiektem i przyczyną (np. statystyki, schema change), a następnie zestawić to z obserwowaną niestabilnością planów w DMV.
Jak rozpoznać parameter sniffing i kiedy prowadzi do skoków CPU w określonych oknach czasu?
Parameter sniffing w SQL Server polega na tym, że przy pierwszej kompilacji zapytania optymalizator „wącha” (sniffuje) konkretne wartości parametrów i na ich podstawie dobiera plan wykonania, który następnie jest buforowany i ponownie używany dla innych wartości. Problem pojawia się wtedy, gdy rozkład danych jest nierówny, a różne wartości parametrów wymagają radykalnie innego planu (np. dla jednych selektywny seek, dla innych skan i inna strategia join). W efekcie „zły” plan może działać znacznie dłużej i zużywać nieproporcjonalnie dużo CPU, mimo że samo zapytanie jest to samo.
Rozpoznanie, że skoki CPU w określonych oknach czasu wynikają z parameter sniffingu, sprowadza się do znalezienia korelacji między (1) ponownym użyciem tego samego planu z cache i (2) okresowym uruchamianiem zapytania z „nietypową” wartością parametru. Typowy wzorzec to sytuacja, w której w określonej porze uruchamia się job, raport, integracja lub proces użytkownika z inną charakterystyką parametrów niż zwykły ruch. Ta jedna kompilacja (albo wymuszone przeplanowanie) może wstawić do cache plan optymalny dla tej nietypowej wartości, a potem przez pewien czas zapytanie jest wykonywane z tym samym planem dla wielu innych wartości, co podbija CPU. „Okno czasu” powstaje, bo plan pozostaje w cache do momentu kolejnej kompilacji/eksmitowania, a nie tylko w chwili samego uruchomienia zadania.
Najbardziej charakterystyczne symptomy diagnostyczne to: stały query_hash (logicznie to samo zapytanie) przy dużej zmienności czasu CPU/elapsed oraz ten sam query_plan_hash wykorzystywany w momentach dobrego i złego zachowania. Dodatkowo w samym planie można potwierdzić sniffing: w XML planu zwykle widać sekcję z „parametrami skompilowanymi” (np. ParameterCompiledValue) oraz porównanie estymacji do rzeczywistości (duże rozjazdy między estymowaną a faktyczną liczbą wierszy na kluczowych operatorach). Jeśli w oknie skoku CPU dominują wykonania tego samego zapytania z identycznym planem, ale z innym rozkładem zwracanych wierszy (raz mało, raz bardzo dużo), a plan jest wyraźnie dopasowany do jednego z tych przypadków, to jest to silna wskazówka na parameter sniffing.
Parameter sniffing prowadzi do skoków CPU szczególnie wtedy, gdy w danym oknie czasu zachodzi jedno z dwóch zdarzeń: plan zostaje skompilowany z „ciężką” wartością parametru (np. obejmującą dużą część danych), a następnie jest masowo używany przez typowe, częstsze wywołania; albo odwrotnie, plan powstaje dla bardzo selektywnej wartości i później trafia na wywołania obejmujące duży wolumen, co wymusza nieoptymalne operacje (np. zbyt wiele lookupów, kosztowne zagnieżdżone pętle). W obu przypadkach skok CPU jest okresowy, bo związany z momentem pojawienia się tego „nietypowego” wywołania oraz z czasem życia planu w cache.
Jak statystyki i automatyczne aktualizacje potrafią wywołać nagłe zmiany planów i wzrost CPU?
Optymalizator SQL Server dobiera plan wykonania na podstawie statystyk, które opisują rozkład danych w kolumnach i indeksach. Gdy statystyki są nieaktualne, estymacje liczby wierszy (cardinality) mogą być błędne, ale plan bywa „stabilny” dopóki nic nie wymusi ponownej kompilacji. W momencie automatycznej aktualizacji statystyk (AUTO_UPDATE_STATISTICS) lub ich aktualizacji wykonanej ręcznie, optymalizator może przy następnej kompilacji uznać, że wcześniejszy plan nie jest już optymalny i wygenerować nowy, czasem radykalnie inny.
Takie nagłe zmiany planu potrafią skokowo podnieść CPU, bo nawet niewielka zmiana estymacji może przełączyć strategię wykonania na bardziej kosztowną procesorowo: np. z joinu zagnieżdżonego na hash join, z seeków na skany, z równoległością (lub bez) w innym miejscu planu. Dodatkowo aktualizacja statystyk może wywołać rekompilację zapytań/procedur zależnych od tych statystyk; sama kompilacja zużywa CPU, a w krótkim oknie czasowym może dotyczyć wielu planów, jeśli aplikacja odpala dużo podobnych zapytań.
W praktyce „stałe pory” skoków wynikają z tego, że aktualizacje statystyk oraz zadania utrzymaniowe (np. przebudowa/reorganizacja indeksów) często są harmonogramowane i pośrednio wpływają na statystyki. Rebuild indeksu aktualizuje statystyki indeksu z pełnym skanem, a to zwiększa prawdopodobieństwo zmiany planu po kolejnym użyciu zapytania. W efekcie po takim zdarzeniu możesz zobaczyć jednocześnie: falę rekompilacji, nowe plany w cache oraz wyraźnie wyższe zużycie CPU na wykonaniu zapytań, które wcześniej działały poprawnie.
Jakie antywzorce w zapytaniach najczęściej powodują skany, rozdmuchane memory grants i wysokie CPU?
Najczęstsze problemy wynikają z tego, że optymalizator nie może użyć selektywnego dostępu po indeksie (kończy się skanami i dużą liczbą wierszy do przetworzenia) albo musi wykonać kosztowne operacje wymagające pamięci roboczej (sort/aggregate/hash), co winduje memory grant i CPU. Poniżej najtypowsze antywzorce, które regularnie prowadzą do tych objawów.
- Niesargalne predykaty – funkcje lub przekształcenia na kolumnie w
WHERE/JOIN(np.WHERE CONVERT(date, Data) = @d,WHERE ISNULL(Kol, 0) = 1,WHERE LEFT(Kod, 3) = 'ABC'). Takie warunki utrudniają użycie indeksu i często kończą się skanem, a przez to większym wolumenem danych do dalszych operatorów. - Brak ograniczeń lub „puste” filtry – zapytania pobierające szeroki zakres danych (np. brak sensownego
WHERE, filtry o bardzo niskiej selektywności, pobieranie „wszystkiego” i filtrowanie dopiero później). To generuje duże przepływy w planie i podbija koszt CPU, zwłaszcza gdy po drodze są sorty/aggregacje. - Sortowanie i paginacja na dużych zbiorach –
ORDER BYbez indeksu wspierającego porządek, zwłaszcza zOFFSET/FETCHlub gdy najpierw trzeba zbudować duży zbiór wynikowy. Operator Sort potrafi żądać dużych grantów pamięci; przy niedoszacowaniu pojawiają się rozlania do tempdb, a przy przeszacowaniu – „rozdmuchane” memory grants blokujące współbieżność. - Duże agregacje i deduplikacja –
GROUP BY,DISTINCT,UNION(w przeciwieństwie doUNION ALL) na dużych danych. Często skutkuje Hash Aggregate lub Sort + Stream Aggregate, co wymaga pamięci roboczej i podbija CPU; przy błędnej estymacji kardynalności memory grant rośnie nieadekwatnie. - Nietrafione złączenia powodujące eksplozję liczby wierszy – brak warunku złączenia (praktycznie cross join), złączenia po kolumnach o niskiej selektywności, albo złączenia po wyrażeniach. Skutkiem jest lawinowy wzrost wierszy wejściowych dla kolejnych operatorów (hash join/aggregate/sort), co podnosi zarówno CPU, jak i wymagania pamięci.
- OR-y i warunki alternatywne utrudniające dobór indeksu – rozbudowane
ORw predykatach (szczególnie na różnych kolumnach) często prowadzi do planów opartych o skany lub kosztowne operacje łączenia wyników, zamiast precyzyjnych seeków. - Parametryzacja „na ślepo” i wrażliwość na wartości – jedna kompilacja planu dla bardzo różnych rozkładów danych (typowo przy parametrach) może spowodować, że plan będzie dobry dla jednego przypadku, a dla innego wykona skan lub wybierze hash/sort z nieadekwatnym memory grantem. Objawem bywa duża zmienność CPU i skokowe memory grants przy tym samym tekście zapytania.
W praktyce te antywzorce łączą się: skan lub nietrafione złączenie zwiększa liczbę wierszy, a to wymusza duże sorty/hashe, które z kolei żądają dużych memory grants i podnoszą CPU.
Jakie działania naprawcze są najbezpieczniejsze na produkcji: indeks, hint, plan guide, rewrite czy schedule change?
Najbezpieczniejsze na produkcji są te działania, które wprowadzają najmniej trwałych zmian w zachowaniu optymalizatora i mają najmniejszy „promień rażenia” poza jednym problematycznym zapytaniem. W praktyce zwykle najbezpieczniejsza jest zmiana harmonogramu (schedule change), bo nie zmienia semantyki zapytań ani planów, a jedynie ogranicza kolizję z innymi obciążeniami; jest też najłatwiejsza do wycofania. Jeśli jednak problemem jest konkretne zapytanie psujące cache lub generujące skoki CPU, sama zmiana harmonogramu bywa tylko obejściem, nie naprawą.
Dodanie indeksu jest z reguły bezpieczniejsze niż ingerencja w dobór planu (hint/plan guide), o ile indeks wynika z rzeczywistego wzorca dostępu i jest przetestowany pod kątem kosztów ubocznych: wzrostu obciążenia DML (INSERT/UPDATE/DELETE), wydłużenia okien utrzymaniowych, większego zużycia pamięci i miejsca oraz ryzyka pogorszenia innych zapytań przez zmianę selektywności i wyboru planów. Indeks jest zmianą trwałą i globalną, ale pozostaje „w granicach” standardowej pracy optymalizatora.
Rewrite (przepisanie zapytania) bywa bardzo bezpieczne, jeśli nie zmienia logiki biznesowej i jest zweryfikowane testami porównującymi wyniki oraz planem wdrożenia z możliwością szybkiego rollbacku. To jednak najczęściej najdroższe organizacyjnie i czasowo, a ryzyko polega głównie na niezamierzonej zmianie semantyki lub regresji wydajności w innych parametryzacjach danych.
Hint oraz plan guide są zazwyczaj najmniej bezpieczne jako „pierwszy wybór”, bo wymuszają konkretne decyzje planu (np. typ złączenia, indeks, równoległość) i mogą stabilizować zły plan w zmieniających się danych. Dodatkowo plan guide jest trudniejszy w utrzymaniu: potrafi przestać pasować po drobnych zmianach tekstu zapytania, komplikuje diagnostykę („dlaczego optymalizator nie wybrał tego sam?”) i zwiększa ryzyko długoterminowego długu technicznego. Po takie narzędzia warto sięgać dopiero, gdy indeks/rewrite nie są możliwe w rozsądnym czasie, a potrzebujesz szybkiej, odwracalnej stabilizacji zachowania jednego zapytania.
Jeśli masz wybrać kolejność „od najbezpieczniejszego typowo na produkcji” w kontekście skoków CPU o stałych porach, to najczęściej będzie to: schedule change (minimalna ingerencja), następnie indeks (standardowa optymalizacja, ale globalny wpływ), potem rewrite (bezpieczne przy dobrych testach, ale ryzyko semantyczne i koszt wdrożenia), a na końcu hint i plan guide (wymuszanie planu, ryzyko w czasie). Ostateczny wybór zależy od tego, czy potrzebujesz obejścia natychmiast, czy trwałej naprawy oraz jak szeroko dana zmiana może wpłynąć na inne zapytania.
Jak ustawić monitoring, żeby kolejne skoki CPU automatycznie wskazywały podejrzane zapytania?
Żeby kolejne skoki CPU „same” wskazywały podejrzane zapytania, musisz powiązać trzy warstwy obserwacji w jednym, powtarzalnym mechanizmie: metrykę hosta/instancji (CPU w czasie), sygnał z samego silnika SQL (co w danym momencie zużywa CPU) oraz automatyczny zrzut diagnostyczny uruchamiany warunkowo (trigger) w chwili przekroczenia progu. Sam wykres CPU nie wskaże zapytania; dopiero korelacja czasowa z danymi o aktualnie wykonywanych i najkosztowniejszych zapytaniach daje jednoznaczną odpowiedź.
W praktyce konfiguracja sprowadza się do ustawienia alertu na CPU (np. średnia z 1–5 minut powyżej ustalonego progu) i podpięcia pod niego akcji, która w momencie alarmu zbiera identyfikatory zapytań zużywających CPU w tej samej minucie. Silniki SQL dostarczają do tego wbudowane źródła danych (DMV/statystyki wykonania), które pozwalają odczytać: tekst zapytania, identyfikator (np. query_hash/plan_hash), czas i liczbę wykonań, oraz metryki CPU/elapsed/reads. Kluczowe jest, aby zbieranie odbywało się automatycznie „na gorąco” w trakcie skoku, bo po jego ustaniu istotne informacje (sesje, aktywne requesty) mogą zniknąć.
Najbardziej użyteczny wzorzec to zapis do własnej tabeli „incydentów CPU” przy każdym wyzwoleniu alertu: znacznik czasu, aktualne obciążenie CPU, oraz top N zapytań według zużycia CPU w krótkim oknie czasowym. Dzięki temu kolejne skoki będą tworzyć porównywalne rekordy i szybko zobaczysz, czy powtarza się to samo zapytanie (ten sam hash), czy zmienia się tylko plan (inny plan_hash dla tego samego zapytania), co jest typowe przy problemach z cache planów.
Żeby automatyzacja była wiarygodna, zadbaj o dwie rzeczy: rozdzielczość próbkowania (CPU i dane z SQL powinny być zbierane co kilkanaście–kilkadziesiąt sekund, a nie co 5 minut) oraz jednoznaczny identyfikator zapytania niezależny od sesji (hash/ID z widoków statystyk). Wtedy przy następnym skoku wystarczy wejść w zapisany incydent i od razu masz listę podejrzanych zapytań z tego samego przedziału czasu, bez ręcznego „polowania” po wykresach.
Majczęściej zadawane pytania i odpowiedzi odnośnie Skoki CPU o stałych porach w SQL: jak znaleźć zapytania, które psują cache
Najczęściej zdradza to powtarzalność piku w tych samych oknach czasu. Jeśli CPU rośnie regularnie co kilka minut albo o stałej godzinie, a liczba użytkowników nie zmienia się podobnie cyklicznie, częściej chodzi o rotację cache, rekompilacje lub zmianę planu. Dodatkowym sygnałem jest wzrost kompilacji i nowych planów przy podobnym wolumenie zapytań.
Najpierw trzeba zgrać w czasie metryki hosta, dane z silnika SQL i harmonogram jobów. Minimalny zestaw do pierwszej diagnozy to:
- okno czasu skoku CPU na hoście,
- top zapytania lub plany według CPU time w tym samym oknie,
- joby startujące w tym przedziale wraz z krokiem i wykonywanym SQL.
Taka korelacja zwykle pozwala szybko zawęzić listę podejrzanych.
Nie, sam wysoki poziom kompilacji nie wystarcza do potwierdzenia problemu. Znaczenie ma korelacja z pikiem CPU oraz z konkretnymi zapytaniami mającymi wiele planów lub częste przeplanowania. Jeśli razem rosną SQL Compilations/sec, SQL Re-Compilations/sec i zużycie CPU, wtedy podejrzenie problemu z cache i kompilatorem staje się dużo mocniejsze.
Najmocniejszy sygnał to to samo zapytanie działające skrajnie różnie przy podobnym planie. Jeśli identyczny query_hash ma duże wahania CPU i czasu wykonania, a plan był skompilowany dla nietypowej wartości parametru, problemem może być parameter sniffing. W praktyce szukasz zgodności między momentem kompilacji, wartościami parametrów i późniejszym oknem podwyższonego CPU.
Najczęściej widać wiele wariantów planu dla logicznie tego samego zapytania albo plan wyraźnie niedopasowany do danych. Typowe sygnały to:
- dużo różnych plan_handle lub plan_hash dla jednego query_hash,
- duże rozjazdy między estymowaną i faktyczną liczbą wierszy,
- skany, kosztowne sorty, hashe lub nietrafione typy złączeń.
Taki zestaw często wskazuje na niestabilność planowania.
Tak, aktualizacja statystyk może bezpośrednio uruchomić falę zmian planów i wzrost CPU. Po odświeżeniu statystyk optymalizator może uznać wcześniejszy plan za nieoptymalny i skompilować nowy wariant. Jeśli dzieje się to w czasie regularnych zadań utrzymaniowych, efekt bywa widoczny jako stały pik CPU po przebudowie indeksów lub po automatycznej aktualizacji statystyk.
Najczęściej winne są niesargalne predykaty, szerokie skany danych i kosztowne operacje sortowania lub agregacji. W praktyce szczególnie problematyczne są:
- funkcje na kolumnach w WHERE lub JOIN,
- ORDER BY bez indeksu wspierającego porządek,
- duże GROUP BY, DISTINCT i UNION,
- złączenia zwiększające lawinowo liczbę wierszy.
Takie wzorce podnoszą jednocześnie CPU i zapotrzebowanie na pamięć roboczą.
Najlepiej połączyć alert CPU z automatycznym zrzutem top zapytań w chwili przekroczenia progu. Monitoring powinien zapisywać znacznik czasu, obciążenie CPU oraz identyfikatory zapytań i planów, najlepiej query_hash i plan_hash, razem z metrykami CPU. Dzięki temu każdy kolejny incydent daje porównywalną listę podejrzanych bez ręcznego sprawdzania wykresów po fakcie.