sql >> Database teknologi >  >> RDS >> PostgreSQL

PostgreSQL 9.6:Parallel sekventiel scanning

I lang tid var en af ​​de mest kendte mangler ved PostgreSQL evnen til at parallelisere forespørgsler. Med udgivelsen af ​​version 9.6 vil dette ikke længere være et problem. Et stort stykke arbejde er blevet gjort med dette emne, startende fra commit 80558c1, introduktionen af ​​parallel sekventiel scanning, som vi vil se i løbet af denne artikel.

Først skal du være opmærksom på:udviklingen af ​​denne funktion har været kontinuerlig, og nogle parametre har skiftet navn mellem en commit og en anden. Denne artikel er skrevet ved hjælp af en checkout, der blev foretaget den 17. juni, og nogle af de her illustrerede funktioner vil kun være til stede i version 9.6 beta2.

Sammenlignet med 9.5-udgivelsen er nye parametre blevet introduceret i konfigurationsfilen. Disse er:

  • max_parallel_workers_per_gather :antallet af arbejdere, der kan assistere en sekventiel scanning af en tabel;
  • min_parallel_relation_size :den mindste størrelse, som en relation skal have, for at planlæggeren kan overveje brugen af ​​yderligere arbejdere;
  • parallel_setup_cost :planlæggerparameteren, der estimerer omkostningerne ved at instantiere en arbejder;
  • parallel_tuple_cost :planlæggerparameteren, der estimerer omkostningerne ved at overføre en tupel fra en arbejder til en anden;
  • force_parallel_mode :parameter nyttig til test, stærk parallelitet og også en forespørgsel, hvor planlæggeren ville fungere på andre måder.

Lad os se, hvordan de ekstra medarbejdere kan bruges til at fremskynde vores forespørgsler. Vi opretter en testtabel med et INT-felt og hundrede millioner poster:

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 har max_parallel_workers_per_gather indstillet til 2 som standard, for hvilke to arbejdere vil blive aktiveret under en sekventiel scanning.

En simpel sekventiel scanning præsenterer ingen nyheder:

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)

Faktisk tilstedeværelsen af ​​en WHERE klausul er påkrævet for parallelisering:

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)

Vi kan gå tilbage til den forrige handling og observere forskelsindstillingen max_parallel_workers_per_gather til 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)

En tid 2,5 gange større.

Planlæggeren anser ikke altid en parallel sekventiel scanning for at være den bedste mulighed. Hvis en forespørgsel ikke er selektiv nok, og der er mange tuples at overføre fra arbejder til arbejder, foretrækker den måske en "klassisk" sekventiel scanning:

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)

Faktisk, hvis vi forsøger at fremtvinge en parallel sekventiel scanning, får vi et dårligere resultat:

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)

Antallet af arbejdere kan øges op til max_worker_processes (standard:8). Vi gendanner værdien af ​​parallel_tuple_cost og vi ser, hvad der sker ved at øge max_parallel_workers_per_gather til 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)

Selvom PostgreSQL kunne bruge op til 8 arbejdere, har det kun instantieret seks. Dette skyldes, at Postgres også optimerer antallet af arbejdere i henhold til tabellens størrelse og min_parallel_relation_size . Antallet af arbejdere, der stilles til rådighed af postgres, er baseret på en geometrisk progression med 3 som fælles forhold 3 og min_parallel_relation_size som skalafaktor. Her er et eksempel. I betragtning af standardparameteren på 8 MB:

Størrelse Medarbejder
<8MB 0
<24MB 1
<72MB 2
<216MB 3
<648MB 4
<1944MB 5
<5822MB 6
... ...

Vores bordstørrelse er 3458 MB, så 6 er det maksimale antal tilgængelige medarbejdere.

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

Til sidst vil jeg give en kort demonstration af de forbedringer, der er opnået gennem denne patch. Når vi kører vores forespørgsel med et stigende antal voksende arbejdere, opnår vi følgende resultater:

Medarbejdere Tid
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

Vi kan se, at tiderne forbedres dramatisk, indtil du når en tredjedel af startværdien. Det er også enkelt at forklare det faktum, at vi ikke ser forbedringer mellem brugen af ​​3 og 6 arbejdere:Maskinen, som testen blev kørt på, har 4 CPU'er, så resultaterne er stabile efter at have tilføjet 3 flere arbejdere til den oprindelige proces .

Endelig har PostgreSQL 9.6 sat scenen for forespørgselsparallelisering, hvor parallel sekventiel scanning kun er det første store resultat. Vi vil også se, at sammenlægninger i 9.6 er blevet paralleliseret, men det er information til en anden artikel, som vil blive frigivet i de kommende uger!


  1. Returner rækker, der indeholder numeriske værdier i Oracle

  2. 2 måder at sammenkæde strenge og tal i MariaDB

  3. Data fra oracle i utf-8 med php

  4. Sådan vælger du de rigtige datatyper