sql >> Databáze >  >> RDS >> PostgreSQL

PostgreSQL 9.6:Paralelní sekvenční skenování

Po dlouhou dobu byla jedním z nejznámějších nedostatků PostgreSQL schopnost paralelizovat dotazy. S vydáním verze 9.6 to již nebude problém. Na tomto tématu byla odvedena skvělá práce, počínaje odevzdáním 80558c1, zavedením paralelního sekvenčního skenování, které uvidíme v průběhu tohoto článku.

Nejprve musíte vzít na vědomí:vývoj této funkce byl nepřetržitý a některé parametry změnily názvy mezi odevzdáním a jiným. Tento článek byl napsán pomocí pokladny provedené 17. června a některé zde znázorněné funkce budou k dispozici pouze ve verzi 9.6 beta2.

Oproti verzi 9.5 byly do konfiguračního souboru zavedeny nové parametry. Jsou to:

  • max_parallel_workers_per_gather :počet pracovníků, kteří mohou pomoci sekvenčnímu skenování stolu;
  • min_parallel_relation_size :minimální velikost, kterou musí mít vztah, aby plánovač zvážil použití dalších pracovníků;
  • parallel_setup_cost :parametr plánovače, který odhaduje náklady na vytvoření instance pracovníka;
  • parallel_tuple_cost :parametr plánovače, který odhaduje náklady na převod n-tice od jednoho pracovníka k druhému;
  • force_parallel_mode :parametr užitečný pro testování, silný paralelismus a také dotaz, ve kterém by plánovač fungoval jinak.

Podívejme se, jak lze využít další pracovníky k urychlení našich dotazů. Vytvoříme testovací tabulku s polem INT a sto miliony záznamů:

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 má max_parallel_workers_per_gather standardně nastaveno na 2, pro které budou během sekvenčního skenování aktivováni dva pracovníci.

Jednoduché sekvenční skenování nepředstavuje žádné novinky:

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)

Ve skutečnosti přítomnost WHERE pro paralelizaci je vyžadována klauzule:

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)

Můžeme se vrátit k předchozí akci a pozorovat rozdíly v nastavení max_parallel_workers_per_gather na 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)

Čas 2,5krát delší.

Plánovač ne vždy považuje paralelní sekvenční skenování za nejlepší možnost. Pokud dotaz není dostatečně selektivní a existuje mnoho n-tic k přenosu z pracovníka na pracovníka, může preferovat „klasické“ sekvenční skenování:

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)

Ve skutečnosti, pokud se pokusíme vynutit paralelní sekvenční skenování, dostaneme horší výsledek:

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)

Počet pracovníků lze zvýšit až na max_worker_processes (výchozí:8). Obnovujeme hodnotu parallel_tuple_cost a uvidíme, co se stane zvýšením 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)

Přestože PostgreSQL mohl používat až 8 pracovníků, vytvořil instanci pouze šesti. Postgres totiž také optimalizuje počet pracovníků podle velikosti stolu a min_parallel_relation_size . Počet pracovníků zpřístupněných postgresem je založen na geometrickém postupu se 3 jako společný poměr 3 a min_parallel_relation_size jako faktor měřítka. Zde je příklad. S ohledem na výchozí parametr 8 MB:

Velikost Pracovník
<8 MB 0
<24 MB 1
<72 MB 2
<216 MB 3
<648 MB 4
<1944 MB 5
<5822 MB 6

Velikost naší tabulky je 3458 MB, takže 6 je maximální počet dostupných pracovníků.

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

Nakonec uvedu krátkou ukázku vylepšení dosažených prostřednictvím tohoto patche. Spuštěním našeho dotazu s rostoucím počtem rostoucích pracovníků získáme následující výsledky:

Zaměstnanci Čas
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

Vidíme, že časy se dramaticky zlepšují, dokud nedosáhnete třetiny původní hodnoty. Je také jednoduché vysvětlit skutečnost, že nevidíme zlepšení mezi použitím 3 a 6 pracovníků:stroj, na kterém byl test spuštěn, má 4 CPU, takže výsledky jsou stabilní po přidání dalších 3 pracovníků k původnímu procesu .

Konečně, PostgreSQL 9.6 připravil půdu pro paralelizaci dotazů, ve které je paralelní sekvenční skenování pouze prvním skvělým výsledkem. Uvidíme také, že v 9.6 byly agregace paralelizovány, ale to je informace pro další článek, který vyjde v nadcházejících týdnech!


  1. Optimalizujte skupinový maximální dotaz

  2. Mýty o výkonu:Zkrácení nelze vrátit zpět

  3. Vypočítejte decil z aktuálnosti v MySQL

  4. jak vypočítat podobnost mezi dvěma řetězci v MYSQL