0Pricing
SQL Interview Prep · Lekcja

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: 25000kB

Czę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: 9500000

Lista 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

  1. Odczytywanie planu EXPLAIN
  2. Seq Scan a Index Scan i Index-Only
  3. Algorytmy złączeń: Nested Loop, Hash, Merge
  4. Wykrywanie i naprawianie wolnych zapytań
← Powrót do SQL Interview Prep