Co dokładnie wyjaśnia PostgreSQL?


116

Wynik wyjaśniający MySQL jest dość prosty. PostgreSQL jest trochę bardziej skomplikowany. Nie udało mi się znaleźć dobrego źródła, które by to wyjaśniło.

Czy możesz opisać, co dokładnie mówi wyjaśnienie lub przynajmniej wskazać mi dobre źródło informacji?

Odpowiedzi:


50

Pomocne może też być wyjaśnienie_EXPLAIN.pdf .


65
Jestem zdziwiony, dlaczego ludzie myślą, że płyty ślizgowe są dobrą dokumentacją techniczną. Film z wykładu może być pomocny, ale gęstość informacji na tej prezentacji jest bardzo bliska zeru. Na pierwszych sześciu slajdach (1/5 całości) znajduje się dokładnie 1 zdanie treści technicznej: „• EXPLAIN działa na każdym DML, a nie tylko SELECT (tj. UPDATE, DELETE i INSERT)”. Moim największym nieporozumieniem jest to, co oznacza czas „uruchamiania”, a to nie jest nigdzie wyjaśnione na tych ~ 30 slajdach.
Mark E. Haase

80

Część, która zawsze wydawała mi się myląca, to koszt uruchomienia w porównaniu z całkowitym kosztem. Google to za każdym razem, gdy o tym zapominam, co prowadzi mnie z powrotem tutaj, co nie wyjaśnia różnicy, dlatego piszę tę odpowiedź. Oto, co zebrałem z dokumentacji PostgresaEXPLAIN , wyjaśnione tak , jak rozumiem.

Oto przykład z aplikacji zarządzającej forum:

EXPLAIN SELECT * FROM post LIMIT 50;

Limit  (cost=0.00..3.39 rows=50 width=422)
  ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

Oto graficzne wyjaśnienie z PgAdmin:

Graficzne wyjaśnienie pierwszego zapytania

(Kiedy używasz PgAdmin, możesz wskazać myszą komponent, aby odczytać szczegóły kosztów).

Koszt jest przedstawiany jako krotka, np. Koszt LIMITjest, cost=0.00..3.39a koszt sekwencyjnego skanowania postto cost=0.00..15629.12. Pierwsza liczba w krotce to koszt uruchomienia, a druga liczba to całkowity koszt . Ponieważ użyłem, EXPLAINa nie EXPLAIN ANALYZE, koszty te są szacunkowe, a nie rzeczywiste.

  • Koszt uruchomienia to trudna koncepcja. To nie tylko reprezentują ilość czasu przed tym elementem startów . Reprezentuje czas między rozpoczęciem wykonywania składnika (wczytywanie danych) a momentem, w którym komponent wyprowadza swój pierwszy wiersz .
  • Całkowity koszt to cały czas wykonywania komponentu, od momentu rozpoczęcia wczytywania danych do zakończenia zapisywania danych wyjściowych.

Jako komplikacja, koszt każdego węzła „nadrzędnego” obejmuje koszt jego węzłów podrzędnych. W reprezentacji tekstowej drzewo jest reprezentowane przez wcięcie, np. LIMITJest węzłem macierzystym i Seq Scanjego dzieckiem. W reprezentacji PgAdmin strzałki wskazują kierunek od dziecka do rodzica - kierunek przepływu danych - co może być sprzeczne z intuicją, jeśli znasz teorię grafów.

Dokumentacja mówi, że koszty obejmują wszystkie węzły podrzędne, ale zauważ, że całkowity koszt rodzica 3.39jest znacznie mniejszy niż całkowity koszt jego dziecka 15629.12. Całkowity koszt nie jest wliczony w cenę, ponieważ komponent taki jak LIMITnie musi przetwarzać całego wkładu. Zobacz EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;przykład w dokumentacji PostgresEXPLAIN .

W powyższym przykładzie czas uruchamiania wynosi zero dla obu składników, ponieważ żaden składnik nie musi wykonywać żadnego przetwarzania przed rozpoczęciem zapisywania wierszy: skanowanie sekwencyjne odczytuje pierwszy wiersz tabeli i emituje go. LIMITCzyta swój pierwszy wiersz, a następnie wysyła go.

Kiedy komponent musiałby dużo przetwarzać, zanim zacząłby wyprowadzać jakiekolwiek wiersze? Istnieje wiele możliwych powodów, ale spójrzmy na jeden jasny przykład. Oto to samo zapytanie co wcześniej, ale teraz zawierające ORDER BYklauzulę:

EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;

Limit  (cost=23283.24..23283.37 rows=50 width=422)
  ->  Sort  (cost=23283.24..23859.27 rows=230412 width=422)
        Sort Key: body
        ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)

I graficznie:

graficzne wyjaśnienie drugiego zapytania

Ponownie, skanowanie sekwencyjne postnie wiąże się z żadnymi kosztami początkowymi: natychmiast rozpoczyna wyświetlanie wierszy. Ale sortowanie wiąże się ze znacznymi kosztami początkowymi, 23283.24ponieważ musi posortować całą tabelę, zanim będzie można wyświetlić nawet jeden wiersz . Całkowity koszt sortowania 23859.27jest tylko nieznacznie wyższy niż koszt uruchomienia, co odzwierciedla fakt, że po posortowaniu całego zbioru danych posortowane dane mogą być emitowane bardzo szybko.

Zauważ, że czas uruchamiania LIMIT 23283.24jest dokładnie równy czasowi uruchamiania tego rodzaju. Nie dzieje się tak dlatego LIMIT, że sam ma długi czas uruchamiania. W rzeczywistości ma sam zerowy czas uruchamiania, ale EXPLAINsumuje wszystkie koszty podrzędne dla każdego rodzica, więc LIMITczas uruchamiania obejmuje sumę czasów uruchamiania jego elementów podrzędnych .

Ta kumulacja kosztów może utrudnić zrozumienie kosztów wykonania każdego pojedynczego komponentu. Na przykład nasz LIMITma zerowy czas uruchamiania, ale na pierwszy rzut oka nie jest to oczywiste. Z tego powodu kilka innych osób połączyło się z wyjaśnieniem.depesz.com , narzędziem stworzonym przez Huberta Lubaczewskiego (aka depesz), które pomaga zrozumieć EXPLAIN, między innymi, odejmując koszty dzieci od kosztów rodziców. Wspomina o innych zawiłościach w krótkim poście na blogu o swoim narzędziu.


4
Dziękuję Ci za to. Chciałbym również dodać wizualizator wyjaśniający, który jeszcze lepiej wyświetla dane wyjściowe (imo). tatiyants.com/pev
Jonathan Porter

Świetna odpowiedź. Twój komentarz, że koszt początkowy obejmuje czas do zwrócenia pierwszego wiersza, pomógł mi zrozumieć, dlaczego koszt początkowy dla Sort nie był tylko 15629.12.
Joel Wigton

43

Wykonuje się od najbardziej wciętego do najmniej wciętego i uważam, że od dołu do góry. (Jeśli więc istnieją dwie sekcje z wcięciem, pierwsza jest wykonywana w pierwszej kolejności w dół strony, a gdy spotykają się z drugą, wykonywana jest reguła łącząca je).

Chodzi o to, że na każdym etapie pojawia się 1 lub 2 zestawy danych, które docierają i są przetwarzane według jakiejś reguły. Jeśli tylko jeden zestaw danych, ta operacja jest wykonywana na tym zestawie danych. (Na przykład zeskanuj indeks, aby dowiedzieć się, jakie wiersze chcesz, przefiltruj zbiór danych lub posortuj go.) Jeśli dwa, to dwa zestawy danych są dwoma elementami, które są dalej wcięte i są połączone regułą, którą widzisz. Znaczenie większości reguł można dość łatwo odgadnąć (szczególnie jeśli wcześniej przeczytałeś kilka planów wyjaśniających), jednak możesz spróbować zweryfikować poszczególne pozycje, przeglądając dokumentację lub (łatwiej), po prostu wrzucając frazę do Google wraz z kilkoma słowami kluczowymi, takimi jak EXPLAIN.

Nie jest to oczywiście pełne wyjaśnienie, ale zapewnia wystarczający kontekst, aby zwykle można było dowiedzieć się, co chcesz. Na przykład rozważ ten plan z rzeczywistej bazy danych:

explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';

------------------------------------------------------------------------------------------------------------------------------------------------------------

 Merge Join  (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
   Merge Cond: (a.orderid = b.orderid)
   ->  Sort  (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
         Sort Key: a.orderid
         Sort Method:  quicksort  Memory: 1695kB
         ->  Bitmap Heap Scan on orderitemattribute a  (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
               Recheck Cond: ((attributeid)::text = 'display-album'::text)
               ->  Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
                     Index Cond: ((attributeid)::text = 'display-album'::text)
   ->  Sort  (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
         Sort Key: b.orderid
         Sort Method:  quicksort  Memory: 76kB
         ->  Bitmap Heap Scan on orderitem b  (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
               Recheck Cond: ((productid)::text = 'ModernBook'::text)
               ->  Bitmap Index Scan on id_orderitem_productid  (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
                     Index Cond: ((productid)::text = 'ModernBook'::text)
 Total runtime: 842.134 ms
(17 rows)

Spróbuj sam to przeczytać i sprawdź, czy ma to sens.

Czytałem, że baza danych najpierw skanuje id_orderitem_productidindeks, używając go do znajdowania wierszy, z których chce orderitem, a następnie sortuje ten zestaw danych za pomocą szybkiego sortowania (użyte sortowanie zmieni się, jeśli dane nie mieszczą się w pamięci RAM), a następnie odkłada to na bok.

Następnie skanuje, orditematt_attributeid_idxaby znaleźć żądane wiersze, orderitemattributea następnie sortuje ten zestaw danych za pomocą szybkiego sortowania.

Następnie pobiera oba zestawy danych i łączy je. (Łączenie przez scalanie to rodzaj operacji „kompresowania”, w której dwa posortowane zbiory danych są równoległe i emituje połączone wiersze, gdy są zgodne).

Jak powiedziałem, przechodzisz przez wewnętrzną część planu do części zewnętrznej, od dołu do góry.



13

PgAdmin pokaże graficzną reprezentację planu wyjaśniania. Przełączanie się między nimi i z powrotem może naprawdę pomóc ci zrozumieć, co oznacza reprezentacja tekstu. Jeśli jednak chcesz tylko wiedzieć, co się dzieje, możesz po prostu zawsze używać GUI.



0

Jeśli zainstalujesz pgadmin, jest przycisk Wyjaśnij, który oprócz wyświetlania tekstu rysuje diagramy tego, co się dzieje, pokazując filtry, sortowanie i scalanie podzbiorów, które uważam za bardzo przydatne do zobaczenia, co się dzieje.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.