PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Optymalizacja zapytań w PostgreSQL. WYJAŚNIJ podstawy – część 3

Kontynuuję serię artykułów na temat podstaw EXPLAIN w PostgreSQL, która jest krótką recenzją Understanding EXPLAIN autorstwa Guillaume Lelarge.
Aby lepiej zrozumieć problem, gorąco polecam przejrzenie oryginalnego „Understanding EXPLAIN” Guillaume Lelarge i przeczytaj mój pierwszy i drugi artykuł.

ZAMÓW PRZEZ

DROP INDEX foo_c1_idx;
EXPLAIN (ANALYZE) SELECT * FROM foo ORDER BY c1;

Najpierw wykonujesz skanowanie sekwencyjne (Seq Scan) tabeli foo, a następnie wykonujesz sortowanie (Sort). Znak -> komendy EXPLAIN wskazuje hierarchię kroków (węzeł). Im wcześniej krok zostanie wykonany, tym większe ma wcięcie.

Klucz sortowania jest warunkiem sortowania.

Metoda sortowania:scalanie zewnętrzne Dysk podczas sortowania używany jest plik tymczasowy na dysku o pojemności 4592 kB.
Sprawdź za pomocą opcji BUFORÓW:

DROP INDEX foo_c1_idx;
EXPLAIN (ANALYZE) SELECT * FROM foo ORDER BY c1;

Rzeczywiście, linia temp read=5745 write=5745 oznacza, że ​​45960Kb (5745 bloków po 8Kb każdy) zostało zapisanych i odczytanych w pliku tymczasowym. Operacje z 8334 blokami zostały wykonane w pamięci podręcznej.

Operacje na systemie plików są wolniejsze niż operacje w pamięci RAM.

Spróbujmy zwiększyć pojemność pamięci work_mem:

SET work_mem TO '200MB';
EXPLAIN (ANALYZE) SELECT * FROM foo ORDER BY c1;

Metoda sortowania:quicksort Pamięć:102702kB – całe sortowanie zostało wykonane w pamięci RAM.
Indeks wygląda następująco:

CREATE INDEX ON foo(c1);
EXPLAIN (ANALYZE) SELECT * FROM foo ORDER BY c1;

Pozostało nam tylko skanowanie indeksów, co znacząco wpłynęło na szybkość zapytania.

LIMIT

Usuń wcześniej utworzony indeks:

DROP INDEX foo_c2_idx1;
EXPLAIN (ANALYZE,BUFFERS)
  SELECT * FROM foo WHERE c2 LIKE 'ab%';

Zgodnie z oczekiwaniami używane są sekwencyjne skanowanie i filtrowanie.

EXPLAIN (ANALYZE,BUFFERS)
SELECT * FROM foo WHERE c2 LIKE 'ab%' LIMIT 10;

Seq Scan odczytuje wiersze tabeli i porównuje je (Filtr) z warunkiem. Gdy tylko 10 rekordów spełni warunek, skanowanie zostanie zakończone. W naszym przypadku, aby uzyskać 10 wierszy wyników, musieliśmy odczytać tylko 3063 rekordów, a nie całą tabelę. 3053 wierszy o tej liczbie zostało odrzuconych (wiersze usunięte przez filtr).
To samo dzieje się ze skanowaniem indeksu.

DOŁĄCZ

Utwórz nową tabelę i wygeneruj dla niej statystyki:

CREATE TABLE bar (c1 integer, c2 boolean);
INSERT INTO bar
  SELECT i, i%2=1
  FROM generate_series(1, 500000) AS i;
ANALYZE bar;

Zapytanie dla dwóch tabel wygląda następująco:

EXPLAIN (ANALYZE)
SELECT * FROM foo JOIN bar ON foo.c1=bar.c1;

Najpierw skan sekwencyjny (Seq Scan) odczytuje tabelę słupków. Dla każdego wiersza obliczany jest skrót (Hash).

Następnie skanuje tabelę foo i dla każdego wiersza obliczany jest hash, który jest porównywany (Hash Join) z hashem tabeli słupkowej według warunku Hash Cond. Jeśli pasują, wyprowadzany jest łańcuch wynikowy.

18067kB pamięci jest używane do przechowywania skrótów dla paska.

Dodaj indeks:

CREATE INDEX ON bar(c1);
EXPLAIN (ANALYZE)
SELECT * FROM foo JOIN bar ON foo.c1=bar.c1;

Hash nie jest już używany. Scalanie łączenia i skanowania indeksów na indeksach obu tabel znacznie poprawia wydajność.

LEWY DOŁĄCZ:

EXPLAIN (ANALYZE)
SELECT * FROM foo LEFT JOIN bar ON foo.c1=bar.c1;

Skanowanie sekwencji?

Zobaczmy, jaki wynik uzyskamy, jeśli wyłączymy Seq Scan.

SET enable_seqscan TO off; 
EXPLAIN (ANALYZE)
SELECT * FROM foo LEFT JOIN bar ON foo.c1=bar.c1;

Według harmonogramu używanie indeksów jest bardziej kosztowne niż używanie skrótów. Jest to możliwe przy odpowiednio dużej ilości przydzielonej pamięci. Czy pamiętasz, jak zwiększaliśmy work_mem?

Jeśli jednak nie masz wystarczającej ilości pamięci, harmonogram będzie zachowywał się inaczej:

SET work_mem TO '15MB';
SET enable_seqscan TO ON; 
EXPLAIN (ANALYZE)
SELECT * FROM foo LEFT JOIN bar ON foo.c1=bar.c1;

Jeśli wyłączymy skanowanie indeksu, jaki wynik zostanie wyświetlony WYJAŚNIJ?

SET work_mem TO '15MB';
SET enable_indexscan TO off; 
EXPLAIN (ANALYZE)
SELECT * FROM foo LEFT JOIN bar ON foo.c1=bar.c1;

Partie:2 ma zwiększony koszt. Cały hasz nie mieścił się w pamięci; musieliśmy podzielić go na dwie paczki po 9045kB.

Dziękuję za przeczytanie moich artykułów! Mam nadzieję, że były przydatne. Jeśli masz jakieś uwagi lub uwagi, daj mi znać.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Typowe błędy podczas migracji baz danych PostgreSQL ze środowiska lokalnego do AWS RDS

  2. Jak zaktualizować wiele kolumn w PostgreSQL

  3. W obronie sar (i jak to skonfigurować)

  4. Zmienne zdefiniowane przez użytkownika w PostgreSQL

  5. Jak stworzyć rozszerzenie postgres wewnątrz kontenera?