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

PostgreSQL 9.6:równoległe skanowanie sekwencyjne

Przez długi czas jednym z najbardziej znanych mankamentów PostgreSQL była możliwość zrównoleglania zapytań. Wraz z wydaniem wersji 9.6 nie będzie to już problemem. Wykonano w tym temacie świetną robotę, począwszy od zatwierdzenia 80558c1, wprowadzenia równoległego skanowania sekwencyjnego, które zobaczymy w tym artykule.

Po pierwsze, musisz wziąć pod uwagę:rozwój tej funkcji był ciągły, a niektóre parametry zmieniły nazwy między zatwierdzeniem a innym. Ten artykuł został napisany przy kasie wykonanej 17 czerwca, a niektóre z przedstawionych tu funkcji będą dostępne tylko w wersji 9.6 beta2.

W porównaniu do wersji 9.5 w pliku konfiguracyjnym wprowadzono nowe parametry. Są to:

  • max_parallel_workers_per_gather :liczba pracowników, którzy mogą asystować przy sekwencyjnym skanowaniu tabeli;
  • min_parallel_relation_size :minimalny rozmiar, jaki musi mieć relacja, aby planista rozważył wykorzystanie dodatkowych pracowników;
  • parallel_setup_cost :parametr planisty, który szacuje koszt wystąpienia pracownika;
  • parallel_tuple_cost :parametr planisty, który szacuje koszt przeniesienia krotki od jednego pracownika do drugiego;
  • force_parallel_mode :parametr przydatny do testowania, silna równoległość, a także zapytanie, w którym planista działałby w inny sposób.

Zobaczmy, jak można wykorzystać dodatkowych pracowników do przyspieszenia naszych zapytań. Tworzymy tabelę testową z polem INT i stu milionami rekordów:

postgres=# CREATE TABLE test (i int);
CREATE TABLE
postgres=# INSERT INTO test SELECT generate_series(1,100000000);
INSERT 0 100000000
postgres=# ANALYSE test;
ANALYZE

PostgreSQL ma max_parallel_workers_per_gather domyślnie ustawione na 2, dla którego dwóch pracowników zostanie aktywowanych podczas skanowania sekwencyjnego.

Proste skanowanie sekwencyjne nie przedstawia żadnych nowości:

postgres=# EXPLAIN ANALYSE SELECT * FROM test;
                                                       QUERY PLAN                         
------------------------------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..1442478.32 rows=100000032 width=4) (actual time=0.081..21051.918 rows=100000000 loops=1)
 Planning time: 0.077 ms
 Execution time: 28055.993 ms
(3 rows)

W rzeczywistości obecność WHERE klauzula jest wymagana do zrównoleglania:

postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i=1;
                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..964311.60 rows=1 width=4) (actual time=3.381..9799.942 rows=1 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on test  (cost=0.00..963311.50 rows=0 width=4) (actual time=6525.595..9791.066 rows=0 loops=3)
         Filter: (i = 1)
         Rows Removed by Filter: 33333333
 Planning time: 0.130 ms
 Execution time: 9804.484 ms
(8 rows)

Możemy wrócić do poprzedniej akcji i zaobserwować ustawienie różnic max_parallel_workers_per_gather do 0:

postgres=# SET max_parallel_workers_per_gather TO 0;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i=1;
                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..1692478.40 rows=1 width=4) (actual time=0.123..25003.221 rows=1 loops=1)
   Filter: (i = 1)
   Rows Removed by Filter: 99999999
 Planning time: 0.105 ms
 Execution time: 25003.263 ms
(5 rows)

Czas 2,5 razy większy.

Planista nie zawsze uważa, że ​​równoległe skanowanie sekwencyjne jest najlepszą opcją. Jeśli zapytanie nie jest wystarczająco selektywne i istnieje wiele krotek do przeniesienia od pracownika do pracownika, może preferować „klasyczne” skanowanie sekwencyjne:

postgres=# SET max_parallel_workers_per_gather TO 2;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i<90000000;
                                                      QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..1692478.40 rows=90116088 width=4) (actual time=0.073..31410.276 rows=89999999 loops=1)
   Filter: (i < 90000000)
   Rows Removed by Filter: 10000001
 Planning time: 0.133 ms
 Execution time: 37939.401 ms
(5 rows)

W rzeczywistości, jeśli spróbujemy wymusić równoległe skanowanie sekwencyjne, uzyskamy gorszy wynik:

postgres=# SET parallel_tuple_cost TO 0;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i<90000000;
                                                             QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..964311.50 rows=90116088 width=4) (actual time=0.454..75546.078 rows=89999999 loops=1)
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on test  (cost=0.00..1338795.20 rows=37548370 width=4) (actual time=0.088..20294.670 rows=30000000 loops=3)
         Filter: (i < 90000000)
         Rows Removed by Filter: 3333334
 Planning time: 0.128 ms
 Execution time: 83423.577 ms
(8 rows)

Liczbę pracowników można zwiększyć do max_worker_processes (domyślnie:8). Przywracamy wartość parallel_tuple_cost i widzimy, co się dzieje, zwiększając max_parallel_workers_per_gather do 8.

postgres=# SET parallel_tuple_cost TO DEFAULT ;
SET
postgres=# SET max_parallel_workers_per_gather TO 8;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i=1;
                                                       QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=1000.00..651811.50 rows=1 width=4) (actual time=3.684..8248.307 rows=1 loops=1)
   Workers Planned: 6
   Workers Launched: 6
   ->  Parallel Seq Scan on test  (cost=0.00..650811.40 rows=0 width=4) (actual time=7053.761..8231.174 rows=0 loops=7)
         Filter: (i = 1)
         Rows Removed by Filter: 14285714
 Planning time: 0.124 ms
 Execution time: 8250.461 ms
(8 rows)

Mimo że PostgreSQL może używać do 8 procesów roboczych, utworzył tylko sześć instancji. Dzieje się tak, ponieważ Postgres optymalizuje również liczbę pracowników zgodnie z rozmiarem tabeli i min_parallel_relation_size . Liczba pracowników udostępnionych przez postgres opiera się na postępie geometrycznym z 3 jako wspólnym stosunkiem 3 i min_parallel_relation_size jako współczynnik skali. Oto przykład. Biorąc pod uwagę 8MB domyślnego parametru:

Rozmiar Pracownik
<8 MB 0
<24 MB 1
<72 MB 2
<216 MB 3
<648 MB 4
<1944 MB 5
<5822 MB 6

Rozmiar naszego stołu wynosi 3458 MB, więc 6 to maksymalna liczba dostępnych pracowników.

postgres=# \dt+ test
                    List of relations
 Schema | Name | Type  |  Owner   |  Size   | Description
--------+------+-------+----------+---------+-------------
 public | test | table | postgres | 3458 MB |
(1 row)

Na koniec przedstawię krótką prezentację ulepszeń osiągniętych dzięki tej łatce. Przeprowadzając nasze zapytanie z rosnącą liczbą rosnących pracowników, otrzymujemy następujące wyniki:

Pracownicy Czas
0 24767.848 ms
1 14855,961 ms
2 10415,661 ms
3 8041,187 ms
4 8090,855 ms
5 8082,937 ms
6 8061,939 ms

Widzimy, że czasy dramatycznie się poprawiają, aż do osiągnięcia jednej trzeciej wartości początkowej. Można również łatwo wyjaśnić fakt, że nie widzimy poprawy między wykorzystaniem 3 i 6 pracowników:maszyna, na której przeprowadzono test, ma 4 procesory, więc wyniki są stabilne po dodaniu 3 dodatkowych pracowników do pierwotnego procesu .

Wreszcie, PostgreSQL 9.6 przygotował grunt pod zrównoleglanie zapytań, w którym równoległe skanowanie sekwencyjne jest tylko pierwszym świetnym wynikiem. Zobaczymy również, że w wersji 9.6 agregacje zostały zrównoleglone, ale jest to informacja do innego artykułu, który ukaże się w nadchodzących tygodniach!


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. FUNKCJA DROP bez znajomości liczby/rodzaju parametrów?

  2. pgadmin4 :nie można skontaktować się z serwerem aplikacji postgresql.

  3. Uaktualnić kolumnę PostgreSQL JSON do JSONB?

  4. Prosta zaszyfrowana biblioteka arytmetyczna (SEAL) i pieczęć::Zmienna zaszyfrowanego tekstu

  5. Zwróć wiersze z INSERT z ON CONFLICT bez konieczności aktualizacji