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

WYJAŚNIENIE PostgreSQL – Jakie są koszty zapytań?

Zrozumienie kosztu WYJAŚNIENIA Postgresa

EXPLAIN jest bardzo przydatne do zrozumienia wydajności zapytania Postgres. Zwraca plan wykonania wygenerowany przez planer zapytań PostgreSQL dla danej instrukcji. EXPLAIN polecenie określa, czy tabele, do których odwołuje się instrukcja, będą przeszukiwane za pomocą skanowania indeksu, czy skanowania sekwencyjnego.

Niektóre z pierwszych rzeczy, które zauważysz podczas przeglądania danych wyjściowych EXPLAIN polecenia to statystyki kosztów, więc naturalne jest, aby się zastanawiać, co one oznaczają, w jaki sposób są obliczane i jak są wykorzystywane.

Krótko mówiąc, planer zapytań PostgreSQL szacuje, ile czasu zajmie zapytanie (w dowolnej jednostce), z uwzględnieniem zarówno kosztu uruchomienia, jak i całkowitego kosztu każdej operacji. Więcej o tym później. Gdy ma wiele opcji wykonania zapytania, wykorzystuje te koszty, aby wybrać najtańszą, a zatem, miejmy nadzieję, najszybszą opcję.

W jakiej jednostce są koszty?

Koszty są w dowolnej jednostce. Powszechnym nieporozumieniem jest to, że są one podawane w milisekundach lub w innej jednostce czasu, ale tak nie jest.

Jednostki kosztu są zakotwiczone (domyślnie) do pojedynczej sekwencyjnej strony, która kosztuje 1,0 jednostki (seq_page_cost ). Każdy przetworzony wiersz dodaje 0,01 (cpu_tuple_cost ), a każda niesekwencyjna strona odczytana dodaje 4.0 (random_page_cost ). Takich stałych jest o wiele więcej, z których wszystkie można konfigurować. Ten ostatni jest szczególnie powszechnym kandydatem, przynajmniej na nowoczesnym sprzęcie. Za chwilę przyjrzymy się temu dokładniej.

Koszty uruchomienia

Pierwsze liczby, które widzisz po cost= są znane jako „koszt uruchomienia”. To jest szacunkowy czas potrzebny na pobranie pierwszego wiersza . W związku z tym koszt uruchomienia operacji obejmuje koszt jej dzieci.

W przypadku skanowania sekwencyjnego koszt uruchomienia będzie na ogół bliski zeru, ponieważ może od razu rozpocząć pobieranie wierszy. W przypadku operacji sortowania będzie ona wyższa, ponieważ duża część pracy musi zostać wykonana, zanim wiersze zaczną być zwracane.

Aby spojrzeć na przykład, stwórzmy prostą tabelę testową z 1000 nazw użytkowników:

CREATE TABLE users (
    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    username text NOT NULL);
INSERT INTO users (username)
SELECT 'person' || n
FROM generate_series(1, 1000) AS n;
ANALYZE users;

Przyjrzyjmy się prostemu planowi zapytań z kilkoma operacjami:

EXPLAIN SELECT * FROM users ORDER BY username;

QUERY PLAN                                                    |
--------------------------------------------------------------+
Sort  (cost=66.83..69.33 rows=1000 width=17)                  |
  Sort Key: username                                          |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17)|

W powyższym planie zapytań, zgodnie z oczekiwaniami, szacowany koszt wykonania instrukcji dla Seq Scan to 0.00 , a dla Sort to 66.83 .

Całkowite koszty

Druga statystyka kosztu, po koszcie początkowym i dwóch kropkach, nazywana jest „kosztem całkowitym”. To jest szacunkowy czas potrzebny na zwrócenie wszystkich wierszy .

Spójrzmy ponownie na ten przykładowy plan zapytań:

QUERY PLAN                                                    |
--------------------------------------------------------------+
Sort  (cost=66.83..69.33 rows=1000 width=17)                  |
  Sort Key: username                                          |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17)|

Widzimy, że całkowity koszt Seq Scan operacja to 17.00 . Dla Sort operacji wynosi 69,33, czyli niewiele więcej niż koszt uruchomienia (zgodnie z oczekiwaniami).

Całkowite koszty zazwyczaj obejmują koszty poprzedzających je operacji. Na przykład całkowity koszt powyższej operacji sortowania obejmuje koszt Seq Scan.

Ważnym wyjątkiem jest LIMIT klauzul, których planista używa do oszacowania, czy może przerwać wcześniej. Jeśli potrzebuje tylko niewielkiej liczby wierszy, dla których warunki są wspólne, może obliczyć, że prostszy wybór skanowania jest tańszy (prawdopodobnie szybszy).

Na przykład:

EXPLAIN SELECT * FROM users LIMIT 1;

QUERY PLAN                                                    |
--------------------------------------------------------------+
Limit  (cost=0.00..0.02 rows=1 width=17)                      |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17)|

Jak widać, całkowity koszt raportowany w węźle Seq Scan nadal wynosi 17,00, ale pełny koszt operacji Limit wynosi 0,02. Dzieje się tak, ponieważ planista spodziewa się, że będzie musiał przetworzyć tylko 1 wiersz z 1000, więc w tym przypadku koszt szacuje się na tysięczną całości.

Jak obliczane są koszty

Aby obliczyć te koszty, planer zapytań Postgres wykorzystuje zarówno stałe (niektóre z nich już widzieliśmy), jak i metadane dotyczące zawartości bazy danych. Metadane są często określane jako „statystyki”.

Statystyki są zbierane za pomocą ANALYZE (nie mylić z EXPLAIN parametr o tej samej nazwie) i przechowywane w pg_statistic . Są one również odświeżane automatycznie w ramach automatycznego odkurzania.

Statystyki te obejmują wiele bardzo przydatnych rzeczy, takich jak przybliżona liczba wierszy w każdej tabeli i najczęstsze wartości w każdej kolumnie.

Spójrzmy na prosty przykład, używając tych samych danych zapytania, co poprzednio:

EXPLAIN SELECT count(*) FROM users;

QUERY PLAN                                                   |
-------------------------------------------------------------+
Aggregate  (cost=19.50..19.51 rows=1 width=8)                |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=0)|

W naszym przypadku statystyki planisty sugerowały, że dane do tabeli były przechowywane w obrębie 7 stron (lub bloków) i że zostanie zwróconych 1000 wierszy. Parametry kosztów seq_page_cost , cpu_tuple_cost i cpu_operator_cost pozostawiono domyślnie 1 , 0.01 i 0.0025 odpowiednio.

W związku z tym całkowity koszt Seq Scan został obliczony jako:

Total cost of Seq Scan
= (estimated sequential page reads * seq_page_cost) + (estimated rows returned * cpu_tuple_cost)
= (7 * 1) + (1000 * 0.01)
= 7 + 10.00
= 17.00

A dla Agregatu jako:

Total cost of Aggregate
= (cost of Seq Scan) + (estimated rows processed * cpu_operator_cost) + (estimated rows returned * cpu_tuple_cost)
= (17.00) + (1000 * 0.0025) + (1 * 0.01) 
= 17.00 + 2.50 + 0.01
= 19.51 

Jak planista wykorzystuje koszty

Ponieważ wiemy, że Postgres wybierze plan zapytań o najniższym całkowitym koszcie, możemy to wykorzystać, aby spróbować zrozumieć dokonane przez siebie wybory. Na przykład, jeśli zapytanie nie używa indeksu, którego oczekujesz, możesz użyć ustawień takich jak enable_seqscan masowo zniechęcać do pewnych wyborów planu zapytań. W tym momencie nie powinieneś być zaskoczony, gdy usłyszysz, że takie ustawienia działają poprzez zwiększanie kosztów!
Numery wierszy są niezwykle ważną częścią szacowania kosztów. Służą do obliczania szacunków dla różnych kolejności łączenia, algorytmów łączenia, typów skanowania i nie tylko. Wierszowe szacunki kosztów, które są bardzo nieaktualne, mogą prowadzić do tego, że oszacowanie kosztów jest bardzo nieaktualne, co ostatecznie może skutkować dokonaniem suboptymalnego wyboru planu.

Używanie EXPLAIN ANALYZE do uzyskania planu zapytania

Kiedy piszesz instrukcje SQL w PostgreSQL, ANALYZE polecenie jest kluczem do optymalizacji zapytań, dzięki czemu są szybsze i bardziej wydajne. Oprócz wyświetlania planu zapytania i oszacowań PostgreSQL, EXPLAIN ANALYZE opcja wykonuje zapytanie (uważaj na UPDATE i DELETE !) i pokazuje rzeczywisty czas wykonania i liczbę wierszy dla każdego kroku w procesie wykonywania. Jest to konieczne do monitorowania wydajności SQL.

Możesz użyć EXPLAIN ANALYZE aby porównać szacowaną liczbę wierszy z rzeczywistymi wierszami zwróconymi przez każdą operację.

Spójrzmy na przykład, używając ponownie tych samych danych:

QUERY PLAN                                                                                                 |
-----------------------------------------------------------------------------------------------------------+
Sort  (cost=66.83..69.33 rows=1000 width=17) (actual time=20.569..20.684 rows=1000 loops=1)                |
  Sort Key: username                                                                                       |
  Sort Method: quicksort  Memory: 102kB                                                                    |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17) (actual time=0.048..0.596 rows=1000 loops=1)|
Planning Time: 0.171 ms                                                                                    |
Execution Time: 20.793 ms                                                                                  |

Widzimy, że całkowity koszt wykonania wciąż wynosi 69,33, z czego większość to operacja sortowania, a 17,00 pochodzi ze skanowania sekwencyjnego. Zwróć uwagę, że czas wykonania zapytania wynosi nieco poniżej 21 ms.

Skanowanie sekwencyjne a skanowanie indeksowe

Teraz dodajmy indeks, aby uniknąć kosztownego sortowania całej tabeli:

​​CREATE INDEX people_username_idx ON users (username);

EXPLAIN ANALYZE SELECT * FROM users ORDER BY username;

QUERY PLAN                                                                                                                       |
---------------------------------------------------------------------------------------------------------------------------------+
Index Scan using people_username_idx on users  (cost=0.28..28.27 rows=1000 width=17) (actual time=0.052..1.494 rows=1000 loops=1)|
Planning Time: 0.186 ms                                                                                                          |
Execution Time: 1.686 ms                                                                                                         |

Jak widać, planista zapytań wybrał teraz skanowanie indeksu, ponieważ całkowity koszt tego planu wynosi 28,27 (niższy niż 69,33). Wygląda na to, że skanowanie indeksu było bardziej wydajne niż skanowanie sekwencyjne, ponieważ czas wykonania zapytania wynosi teraz nieco poniżej 2 ms.

Pomaganie planistom w dokładniejszym oszacowaniu

Możemy pomóc planiście dokładniejsze oszacowanie na dwa sposoby:

  1. Pomóż gromadzić lepsze statystyki
  2. Dostosuj stałe używane do obliczeń

Statystyki mogą być szczególnie złe po dużej zmianie danych w tabeli. W związku z tym, podczas ładowania dużej ilości danych do tabeli, możesz pomóc Postgresowi, uruchamiając ręczną ANALYZE na tym. Statystyki również nie są zachowywane podczas aktualizacji głównej wersji, więc jest to kolejny ważny moment, aby to zrobić.

Oczywiście tabele również zmieniają się w czasie, więc dostrojenie ustawień automatycznego odkurzania, aby upewnić się, że działa wystarczająco często, aby sprostać obciążeniu pracą, może być bardzo pomocne.

Jeśli masz problem ze złymi szacunkami dla kolumny z rozkładem skośnym, możesz skorzystać na zwiększeniu ilości informacji gromadzonych przez Postgres za pomocą ALTER TABLE SET STATISTICS polecenie, a nawet default_statistics_target dla całej bazy danych.

Inną częstą przyczyną błędnych oszacowań jest to, że Postgres domyślnie zakłada, że ​​dwie kolumny są niezależne. Możesz to naprawić, prosząc go o zebranie danych korelacji z dwóch kolumn z tej samej tabeli za pomocą rozszerzonych statystyk.

Na froncie ciągłego strojenia jest wiele parametrów, które można dostroić do swojego sprzętu. Zakładając, że korzystasz z dysków SSD, prawdopodobnie zechcesz co najmniej dostroić ustawienie random_page_cost . Wartość domyślna to 4, co jest 4 razy droższe niż seq_page_cost przyjrzeliśmy się wcześniej. Ten stosunek miał sens na obracających się dyskach, ale na dyskach SSD ma tendencję do nadmiernego obciążania losowych operacji we/wy. W związku z tym ustawienie bliższe 1 lub między 1 a 2 może mieć więcej sensu. W ScaleGrid domyślnie przyjmujemy 1.

Czy mogę usunąć koszty z planów zapytań?

Z wielu powodów wymienionych powyżej większość ludzi pozostawia koszty włączone podczas uruchamiania EXPLAIN . Jeśli jednak chcesz, możesz je wyłączyć za pomocą COSTS parametr.

EXPLAIN (COSTS OFF) SELECT * FROM users LIMIT 1;

QUERY PLAN             |
-----------------------+
Limit                  |
  ->  Seq Scan on users|

Wniosek

Podsumowując, koszty w planach zapytań to szacunki Postgresa dotyczące tego, jak długo zajmie zapytanie SQL, w dowolnej jednostce.

Wybiera plan o najniższym całkowitym koszcie na podstawie pewnych konfigurowalnych stałych i zebranych statystyk.

Pomoc w dokładniejszym oszacowaniu tych kosztów jest bardzo ważna, ponieważ pomaga w dokonywaniu właściwych wyborów i utrzymaniu wydajności zapytań.

Chcesz dowiedzieć się więcej o ScaleGrid?

Aby dowiedzieć się więcej o tym, jak ScaleGrid może pomóc w zarządzaniu bazami danych, skontaktuj się z nami, a my pokażemy Ci całą naszą ofertę DBaaS. Dowiedz się więcej o tym, jak ScaleGrid może pozwolić Ci skupić się bardziej na rozwoju produktu, a mniej na zarządzaniu bazami danych.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Heroku Postgres Error:PGError:ERROR:organizacje relacyjne nie istnieją (ActiveRecord::StatementInvalid)

  2. Jak używać (zainstalować) dblink w PostgreSQL?

  3. Jak w Redshift/Postgres zliczyć wiersze spełniające warunek?

  4. Praskie spotkanie PostgreSQL

  5. Moje ulubione rozszerzenia PostgreSQL — część druga