Wykrywanie i naprawianie wolnych zapytań
Lista kontrolna do zadania rekrutacyjnego: „to zapytanie działa wolno, proszę je naprawić”.
Wykrywanie i naprawianie wolnych zapytań to bezpłatna lekcja SQL Interview Prep na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej SQL Interview Prep, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs SQL Interview Prep zawiera 4 lekcji w sumie.
Zadanie „To zapytanie jest wolne — proszę je naprawić”
To najważniejsze zadanie podczas rozmowy: rekruter przedstawia wolne zapytanie wraz z planem EXPLAIN ANALYZE i prosi o zdiagnozowanie problemu. Sprawdza metodę, a nie znajomość zapamiętanych sztuczek.
Dobra odpowiedź opiera się na głośno przedstawionej liście kontrolnej: zmierzyć, odczytać plan, znaleźć dominujący koszt, sformułować hipotezę, zaproponować rozwiązanie i je zweryfikować. Ta lekcja krok po kroku buduje taką listę.
Należy zachować systematyczność i wyjaśniać tok rozumowania — właśnie to zapewnia ocenę na poziomie seniora.
Krok 1: Pomiar za pomocą EXPLAIN ANALYZE
Nie należy zgadywać na podstawie samego SQL. Trzeba uzyskać rzeczywisty plan za pomocą EXPLAIN (ANALYZE, BUFFERS).
ANALYZE podaje rzeczywiste czasy i liczby wierszy, a BUFFERS pokazuje, czy dane są pobierane z pamięci podręcznej, czy z dysku. Razem informują, czy zapytanie jest ograniczone przez CPU, operacje wejścia/wyjścia, czy po prostu wykonuje zbyt dużo pracy.
Warto uruchomić je kilka razy; pierwsze wykonanie może być obciążone kosztem pustej pamięci podręcznej, co zniekształci pomiar czasu.
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders o
JOIN customers c ON c.id = o.customer_id
WHERE o.created_at >= '2026-01-01';Krok 2: Znalezienie dominującego węzła
Nie należy czytać planu od góry do dołu i szukać problemu losowo. Trzeba znaleźć węzeł, w którym faktycznie zużywana jest największa ilość czasu.
Należy obliczyć czas własny każdego węzła: jego całkowity actual time minus czas węzłów potomnych, pomnożony przez loops. Węzeł o największym udziale jest celem analizy; cała reszta to szum.
Podczas rozmowy warto powiedzieć: 80 procent czasu wykonania przypada na ten jeden Seq Scan, dlatego skupiam się właśnie na nim. Optymalizowanie czegokolwiek innego byłoby stratą wysiłku.
Krok 3: Porównanie wartości szacowanych z rzeczywistymi
Przy dominującym węźle należy porównać szacowaną liczbę wierszy z rzeczywistą liczbą wierszy. Duża rozbieżność oznacza, że planista działa po omacku i prawdopodobnie wybrał zły plan (niewłaściwy algorytm złączenia lub metodę dostępu).
Przykład pokazuje zaniżenie estymacji 1000 razy. Zanim zostanie przeprojektowane cokolwiek innego, należy odświeżyć statystyki — to pojedyncze polecenie często bez dodatkowych kosztów naprawia plan.
ANALYZE ponownie oblicza statystyki kolumn, a VACUUM ANALYZE dodatkowo usuwa martwe krotki i aktualizuje mapę widoczności.
-- estimate rows=100, actual rows=120000 -> stale stats
ANALYZE orders;
-- or, for bloated tables:
VACUUM ANALYZE orders;Częsta przyczyna: funkcja na indeksowanej kolumnie
Najczęstszy błąd, który można naprawić: funkcja lub rzutowanie opakowuje kolumnę w WHERE, przez co nie można użyć indeksu, a silnik wykonuje skan sekwencyjny.
Przykład wymusza pełne skanowanie, ponieważ funkcja DATE() jest stosowana do każdego wiersza. Należy przepisać warunek jako predykat zakresowy na surowej kolumnie (w postaci sargowalnej), a indeks na created_at zostanie wykorzystany.
Ta sama zasada dotyczy WHERE lower(email)=...: należy przechowywać znormalizowane dane, odpytywać bezpośrednio kolumnę albo utworzyć indeks wyrażeniowy.
-- Not sargable: index unusable
WHERE DATE(created_at) = '2026-01-01'
-- Sargable: range over the bare column
WHERE created_at >= '2026-01-01'
AND created_at < '2026-01-02'Częsta przyczyna: brak indeksu
Jeśli dominującym węzłem jest Seq Scan z bardzo selektywnym filtrem albo Nested Loop z ogromną wartością loops przy nieindeksowanym kluczu wewnętrznym, rozwiązaniem jest zwykle indeks.
Należy dodać indeks na kolumnie używanej do filtrowania lub złączenia. W przykładzie tworzony jest indeks na customer_id, dzięki czemu złączenie może przełączyć się ze skanów sekwencyjnych na skany indeksu, a planista może wybrać znacznie tańszy plan.
Należy zweryfikować wynik, ponownie uruchamiając EXPLAIN ANALYZE — nie wolno zakładać, że indeks pomógł.
CREATE INDEX idx_orders_customer
ON orders (customer_id);Częsta przyczyna: SELECT * i szerokie wiersze
SELECT * pobiera z dysku i przesyła przez sieć każdą kolumnę, a także uniemożliwia skany wyłącznie po indeksie, ponieważ indeks rzadko obejmuje wszystkie kolumny.
Należy wybierać tylko potrzebne kolumny. Zmniejsza to szerokość wierszy, ogranicza operacje wejścia/wyjścia i może umożliwić skanowanie wyłącznie po indeksie pokrywającym.
Rekruter, który umieszcza w zadaniu SELECT *, chce sprawdzić, czy zostanie to zauważone. Ograniczenie listy kolumn często przynosi szybką i rzeczywistą poprawę w przypadku szerokich tabel.
-- Before
SELECT * FROM orders WHERE customer_id = 42;
-- After: only needed columns (may enable index-only scan)
SELECT order_id, amount FROM orders WHERE customer_id = 42;Częsta przyczyna: zapis danych na dysku
Jeśli węzeł Sort lub Hash zgłasza użycie dysku (Sort Method: external merge Disk: 25000kB lub Batches: > 1), operacja przekroczyła limit work_mem i zapisała dane na dysku.
Możliwe rozwiązania to zwiększenie wartości work_mem dla sesji, ograniczenie liczby wierszy docierających do sortowania lub haszowania (wcześniejsze filtrowanie) albo dodanie indeksu zapewniającego uporządkowanie danych, dzięki czemu sortowanie nie będzie w ogóle potrzebne.
To precyzyjna diagnoza na poziomie seniora, którą rekruterzy doceniają.
Sort (actual rows=2000000 loops=1)
Sort Key: o.amount
Sort Method: external merge Disk: 25000kBCzęsta przyczyna: pobieranie zbyt wielu wierszy
Należy zwrócić uwagę na Rows Removed by Filter: 9500000. Zapytanie odczytało dziesięć milionów wierszy i odrzuciło niemal wszystkie — to klasyczny przykład zmarnowanej pracy.
Rozwiązania obejmują dodanie indeksu, aby filtr był stosowany podczas dostępu do danych (a nie później), zwiększenie selektywności predykatu lub wcześniejsze zastosowanie filtrowania w zapytaniu, tak aby mniej wierszy przepływało w górę drzewa.
Zasada jest prosta: wykonywać jak najmniej pracy oraz filtrować tak wcześnie i tak tanio, jak to możliwe.
Seq Scan on events
Filter: (event_type = 'purchase')
Rows Removed by Filter: 9500000Lista kontrolna diagnostyki
Warto powtórzyć to podczas rozmowy, aby nie stracić właściwego kierunku:
- Zmierz za pomocą
EXPLAIN (ANALYZE, BUFFERS). - Zlokalizuj węzeł zużywający najwięcej czasu.
- Porównaj szacowaną i rzeczywistą liczbę wierszy, najpierw popraw nieaktualne statystyki.
- Sprawdź sargowalność, usuwając funkcje z filtrowanych kolumn.
- Dodaj indeksy dla selektywnych filtrów i kluczy złączeń.
- Ogranicz liczbę kolumn, unikaj
SELECT *. - Obserwuj zapisywanie danych na dysku i pobieranie zbyt wielu wierszy.
- Zweryfikuj wynik, ponownie uruchamiając plan.
Połączenie wszystkiego w całość
Proszę głośno omówić pełny przykład. Plan pokazuje Seq Scan na tabeli orders zawierającej 50 mln wierszy, filtr customer_id = 42, wartość Rows Removed by Filter bliską 50 mln oraz estymatę w przybliżeniu zgodną z wartością rzeczywistą.
Diagnoza: filtr selektywny, brak indeksu, a dominującym kosztem jest skanowanie. Naprawa: CREATE INDEX ON orders(customer_id). Po ponownym uruchomieniu plan przełącza się na Index Scan, a czas spada z kilku sekund do mniej niż milisekundy.
Ta pętla: zmierz, zdiagnozuj, napraw, zweryfikuj, jest schematem odpowiedzi na każde pytanie o wolne zapytanie.
CREATE INDEX idx_orders_customer ON orders (customer_id);
EXPLAIN (ANALYZE, BUFFERS)
SELECT order_id, amount FROM orders WHERE customer_id = 42;Szybki test
Zapytanie filtruje dane za pomocą WHERE YEAR(order_date) = 2026, a plan pokazuje pełny Seq Scan mimo istniejącego indeksu B-tree na order_date. Jaka jest najlepsza pierwsza poprawka?
Podsumowanie
Ma już Pan/Pani powtarzalną metodę rozwiązywania zadań dotyczących wolnych zapytań:
- Zawsze mierz wydajność za pomocą
EXPLAIN (ANALYZE, BUFFERS)i skupiaj się na dominującym węźle. - W pierwszej kolejności napraw nieaktualne statystyki, gdy estymaty różnią się od wartości rzeczywistych.
- Twórz sargowalne predykaty, dodawaj indeksy dla selektywnych filtrów i kluczy używanych w złączeniach oraz ograniczaj użycie
SELECT *. - Rozwiązuj problemy z przelewaniem danych na dysk i pobieraniem nadmiarowych danych, a następnie zweryfikuj nowy plan.
Przedstawienie tej listy kontrolnej, zaproponowanie konkretnej zmiany i ponowne uruchomienie planu w celu jej potwierdzenia to odpowiedź na poziomie starszego inżyniera.
Często zadawane pytania
Czy lekcja „Wykrywanie i naprawianie wolnych zapytań” jest bezpłatna?
Tak — pełny tekst „Wykrywanie i naprawianie wolnych zapytań” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu SQL Interview Prep, przejdź na CoddyKit PRO. Kurs SQL Interview Prep zawiera 4 lekcji w sumie.
Co nauczysz się w „Wykrywanie i naprawianie wolnych zapytań”?
Lista kontrolna do zadania rekrutacyjnego: „to zapytanie działa wolno, proszę je naprawić”. Ćwiczysz SQL Interview Prep z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć SQL Interview Prep?
Nie wymagamy żadnego doświadczenia. SQL Interview Prep w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Wykrywanie i naprawianie wolnych zapytań”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji SQL Interview Prep?
Tak. Każda lekcja SQL Interview Prep zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Odczytywanie planu EXPLAIN
- Seq Scan a Index Scan i Index-Only
- Algorytmy złączeń: Nested Loop, Hash, Merge
- Wykrywanie i naprawianie wolnych zapytań