ืฉืื™ืœืชื•ืช ืžืงื‘ื™ืœื•ืช ื‘-PostgreSQL

ืฉืื™ืœืชื•ืช ืžืงื‘ื™ืœื•ืช ื‘-PostgreSQL
ืœืžืขื‘ื“ื™ื ืžื•ื“ืจื ื™ื™ื ื™ืฉ ื”ืจื‘ื” ืœื™ื‘ื•ืช. ื‘ืžืฉืš ืฉื ื™ื, ืืคืœื™ืงืฆื™ื•ืช ืฉื•ืœื—ื•ืช ืฉืื™ืœืชื•ืช ืœืžืื’ืจื™ ืžื™ื“ืข ื‘ืžืงื‘ื™ืœ. ืื ืžื“ื•ื‘ืจ ื‘ืฉืื™ืœืชืช ื“ื•ื— ืขืœ ืžืกืคืจ ืฉื•ืจื•ืช ื‘ื˜ื‘ืœื”, ื”ื™ื ืคื•ืขืœืช ืžื”ืจ ื™ื•ืชืจ ื‘ืขืช ืฉื™ืžื•ืฉ ื‘ืžืกืคืจ ืžืขื‘ื“ื™ื, ื•-PostgreSQL ื”ืฆืœื™ื— ืœืขืฉื•ืช ื–ืืช ืžืื– ื’ืจืกื” 9.6.

ืœืงื— 3 ืฉื ื™ื ืœื™ื™ืฉื ืืช ืชื›ื•ื ืช ื”ืฉืื™ืœืชื” ื”ืžืงื‘ื™ืœื” - ื”ื™ื™ื ื• ืฆืจื™ื›ื™ื ืœืฉื›ืชื‘ ืืช ื”ืงื•ื“ ื‘ืฉืœื‘ื™ื ืฉื•ื ื™ื ืฉืœ ื‘ื™ืฆื•ืข ื”ืฉืื™ืœืชื”. PostgreSQL 9.6 ื”ืฆื™ื’ ืชืฉืชื™ืช ืœืฉื™ืคื•ืจ ื ื•ืกืฃ ืฉืœ ื”ืงื•ื“. ื‘ื’ืจืกืื•ืช ื”ื‘ืื•ืช, ืกื•ื’ื™ื ืื—ืจื™ื ืฉืœ ืฉืื™ืœืชื•ืช ืžื‘ื•ืฆืขื•ืช ื‘ืžืงื‘ื™ืœ.

ื”ื’ื‘ืœื•ืช

  • ืืœ ืชืืคืฉืจ ื‘ื™ืฆื•ืข ืžืงื‘ื™ืœ ืื ื›ืœ ื”ืœื™ื‘ื•ืช ื›ื‘ืจ ืชืคื•ืกื•ืช, ืื—ืจืช ื‘ืงืฉื•ืช ืื—ืจื•ืช ื™ืื˜ื•.
  • ื•ื”ื›ื™ ื—ืฉื•ื‘, ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ ืขื ืขืจื›ื™ WORK_MEM ื’ื‘ื•ื”ื™ื ืžืฉืชืžืฉ ื‘ื”ืจื‘ื” ื–ื™ื›ืจื•ืŸ - ื›ืœ ื”ืฆื˜ืจืคื•ืช ืื• ืžื™ื•ืŸ hash ืชื•ืคืกืช ื–ื™ื›ืจื•ืŸ work_mem.
  • ืœื ื ื™ืชืŸ ืœื”ืื™ืฅ ืฉืื™ืœืชื•ืช OLTP ืขื ื—ื‘ื™ื•ืŸ ื ืžื•ืš ืขืœ ื™ื“ื™ ื‘ื™ืฆื•ืข ืžืงื‘ื™ืœ. ื•ืื ื”ืฉืื™ืœืชื” ืžื—ื–ื™ืจื” ืฉื•ืจื” ืื—ืช, ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ ืจืง ื™ืื˜ ืื•ืชื”.
  • ืžืคืชื—ื™ื ืื•ื”ื‘ื™ื ืœื”ืฉืชืžืฉ ื‘ืžื“ื“ TPC-H. ืื•ืœื™ ื™ืฉ ืœืš ืฉืื™ืœืชื•ืช ื“ื•ืžื•ืช ืœื‘ื™ืฆื•ืข ืžืงื‘ื™ืœ ืžื•ืฉืœื.
  • ืจืง ืฉืื™ืœืชื•ืช SELECT ืœืœื ื ืขื™ืœืช ืคืจื“ื™ืงื˜ื™ื ืžื‘ื•ืฆืขื•ืช ื‘ืžืงื‘ื™ืœ.
  • ืœืคืขืžื™ื ืื™ื ื“ืงืก ื ื›ื•ืŸ ืขื“ื™ืฃ ืขืœ ืกืจื™ืงืช ื˜ื‘ืœื” ืจืฆื™ืคื” ื‘ืžืฆื‘ ืžืงื‘ื™ืœื™.
  • ื”ืฉื”ื™ื™ืช ืฉืื™ืœืชื•ืช ื•ืกืžื ื™ื ืื™ื ื ื ืชืžื›ื™ื.
  • ืคื•ื ืงืฆื™ื•ืช ื—ืœื•ืŸ ื•ืคื•ื ืงืฆื™ื•ืช ืžืฆื˜ื‘ืจื•ืช ืžืกื•ื“ืจื•ืช ืื™ื ืŸ ืžืงื‘ื™ืœื•ืช.
  • ืืชื” ืœื ืžืจื•ื•ื™ื— ื›ืœื•ื ื‘ืขื•ืžืก ื”ืขื‘ื•ื“ื” ืฉืœ ื”-I/O.
  • ืื™ืŸ ืืœื’ื•ืจื™ืชืžื™ ืžื™ื•ืŸ ืžืงื‘ื™ืœื™ื. ืื‘ืœ ืฉืื™ืœืชื•ืช ืขื ืžื™ื•ื ื™ื ื™ื›ื•ืœื•ืช ืœื”ืชื‘ืฆืข ื‘ืžืงื‘ื™ืœ ื‘ื”ื™ื‘ื˜ื™ื ืžืกื•ื™ืžื™ื.
  • ื”ื—ืœืฃ CTE (ืขื ...) ื‘-SELECT ืžืงื•ื ืŸ ื›ื“ื™ ืœืืคืฉืจ ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ.
  • ืขื˜ื™ืคื•ืช ื ืชื•ื ื™ื ืฉืœ ืฆื“ ืฉืœื™ืฉื™ ืื™ื ืŸ ืชื•ืžื›ื•ืช ืขื“ื™ื™ืŸ ื‘ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ (ืื‘ืœ ื”ืŸ ื™ื›ื•ืœื•ืช!)
  • FULL OUTER JOIN ืื™ื ื• ื ืชืžืš.
  • max_rows ืžืฉื‘ื™ืช ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ.
  • ืื ืœืฉืื™ืœืชื” ื™ืฉ ืคื•ื ืงืฆื™ื” ืฉืื™ื ื” ืžืกื•ืžื ืช ื›-PARALLEL SAFE, ื”ื™ื ืชื”ื™ื” ืขื ืฉืจืฉื•ืจ ื™ื—ื™ื“.
  • ืจืžืช ื‘ื™ื“ื•ื“ ื”ืขืกืงืื•ืช SERIALIZABLE ืžืฉื‘ื™ืชื” ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ.

ืกื‘ื™ื‘ืช ื‘ื“ื™ืงื”

ืžืคืชื—ื™ PostgreSQL ื ื™ืกื• ืœื”ืคื—ื™ืช ืืช ื–ืžืŸ ื”ืชื’ื•ื‘ื” ืฉืœ ืฉืื™ืœืชื•ืช ื”ืฉื•ื•ืืช TPC-H. ื”ื•ืจื“ ืืช ื”ื‘ื ืฆ'ืžืจืง ื• ื”ืชืื™ืžื• ืื•ืชื• ืœ-PostgreSQL. ื–ื”ื• ืฉื™ืžื•ืฉ ืœื ืจืฉืžื™ ื‘ืžื‘ื—ืŸ TPC-H - ืœื ืœื”ืฉื•ื•ืืช ืžืกื“ื™ ื ืชื•ื ื™ื ืื• ื—ื•ืžืจื”.

  1. ื”ื•ืจื“ ืืช TPC-H_Tools_v2.17.3.zip (ืื• ื’ืจืกื” ื—ื“ืฉื” ื™ื•ืชืจ) ืž-TPC ืžื—ื•ืฅ ืœืืชืจ.
  2. ืฉื ื” ืืช ื”ืฉื ืฉืœ makefile.suite ืœ-Makefile ื•ืฉื ื” ื›ืžืชื•ืืจ ื›ืืŸ: https://github.com/tvondra/pg_tpch . ืงื•ืžืคืœื• ืืช ื”ืงื•ื“ ืขื ื”ืคืงื•ื“ื” make.
  3. ื”ืคืงืช ื ืชื•ื ื™ื: ./dbgen -s 10 ื™ื•ืฆืจ ืžืกื“ ื ืชื•ื ื™ื ืฉืœ 23 GB. ื–ื” ืžืกืคื™ืง ื›ื“ื™ ืœืจืื•ืช ืืช ื”ื”ื‘ื“ืœ ื‘ื‘ื™ืฆื•ืขื™ื ืฉืœ ืฉืื™ืœืชื•ืช ืžืงื‘ื™ืœื•ืช ื•ืœื ืžืงื‘ื™ืœื•ืช.
  4. ื”ืžืจืช ืงื‘ืฆื™ื tbl ะฒ csv ั for ะธ sed.
  5. ืฉื›ืคืœ ืืช ื”ืžืื’ืจ pg_tpch ื•ืœื”ืขืชื™ืง ืืช ื”ืงื‘ืฆื™ื csv ะฒ pg_tpch/dss/data.
  6. ืฆื•ืจ ืฉืื™ืœืชื•ืช ืขื ืคืงื•ื“ื” qgen.
  7. ื˜ืขืŸ ื ืชื•ื ื™ื ืœืžืกื“ ื”ื ืชื•ื ื™ื ื‘ืืžืฆืขื•ืช ื”ืคืงื•ื“ื” ./tpch.sh.

ืกืจื™ืงื” ืจืฆื™ืคื” ืžืงื‘ื™ืœื”

ื–ื” ืขืฉื•ื™ ืœื”ื™ื•ืช ืžื”ื™ืจ ื™ื•ืชืจ ืœื ื‘ื’ืœืœ ืงืจื™ืื” ืžืงื‘ื™ืœื”, ืืœื ื‘ื’ืœืœ ืฉื”ื ืชื•ื ื™ื ืžืคื•ื–ืจื™ื ืขืœ ืคื ื™ ืœื™ื‘ื•ืช CPU ืจื‘ื•ืช. ื‘ืžืขืจื›ื•ืช ื”ืคืขืœื” ืžื•ื“ืจื ื™ื•ืช, ืงื‘ืฆื™ ื”ื ืชื•ื ื™ื ืฉืœ PostgreSQL ื ืฉืžืจื™ื ื”ื™ื˜ื‘. ืขื ื”ืงืจื™ืื” ืงื“ื™ืžื”, ืืคืฉืจ ืœืงื‘ืœ ื‘ืœื•ืง ื’ื“ื•ืœ ื™ื•ืชืจ ืžืื—ืกื•ืŸ ืžืืฉืจ ื‘ืงืฉื•ืช ื”ื“ืžื•ืŸ PG. ืœื›ืŸ, ื‘ื™ืฆื•ืขื™ ื”ืฉืื™ืœืชื” ืื™ื ื ืžื•ื’ื‘ืœื™ื ืขืœ ื™ื“ื™ ืงืœื˜/ืคืœื˜ ื“ื™ืกืง. ื–ื” ืฆื•ืจืš ืžื—ื–ื•ืจื™ ืžืขื‘ื“ ื›ื“ื™:

  • ืงืจื ืฉื•ืจื•ืช ืื—ืช ื‘ื›ืœ ืคืขื ืžื“ืคื™ ื”ื˜ื‘ืœื”;
  • ื”ืฉื•ื• ืขืจื›ื™ ืžื—ืจื•ื–ืช ื•ืชื ืื™ื WHERE.

ื‘ื•ืื• ื ืจื™ืฅ ืฉืื™ืœืชื” ืคืฉื•ื˜ื” select:

tpch=# explain analyze select l_quantity as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Seq Scan on lineitem (cost=0.00..1964772.00 rows=58856235 width=5) (actual time=0.014..16951.669 rows=58839715 loops=1)
Filter: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)
Rows Removed by Filter: 1146337
Planning Time: 0.203 ms
Execution Time: 19035.100 ms

ื”ืกืจื™ืงื” ื”ืจืฆื™ืคื” ืžื™ื™ืฆืจืช ื™ื•ืชืจ ืžื“ื™ ืฉื•ืจื•ืช ืœืœื ืฆื‘ื™ืจื”, ื›ืš ืฉื”ืฉืื™ืœืชื” ืžื‘ื•ืฆืขืช ืขืœ ื™ื“ื™ ืœื™ื‘ืช ืžืขื‘ื“ ื™ื—ื™ื“ื”.

ืื ืชื•ืกื™ืฃ SUM(), ืืชื” ื™ื›ื•ืœ ืœืจืื•ืช ืฉืฉืชื™ ื–ืจื™ืžื•ืช ืขื‘ื•ื“ื” ื™ืขื–ืจื• ืœื”ืื™ืฅ ืืช ื”ืฉืื™ืœืชื”:

explain analyze select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=1589702.14..1589702.15 rows=1 width=32) (actual time=8553.365..8553.365 rows=1 loops=1)
-> Gather (cost=1589701.91..1589702.12 rows=2 width=32) (actual time=8553.241..8555.067 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=1588701.91..1588701.92 rows=1 width=32) (actual time=8547.546..8547.546 rows=1 loops=3)
-> Parallel Seq Scan on lineitem (cost=0.00..1527393.33 rows=24523431 width=5) (actual time=0.038..5998.417 rows=19613238 loops=3)
Filter: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)
Rows Removed by Filter: 382112
Planning Time: 0.241 ms
Execution Time: 8555.131 ms

ืฆื‘ื™ืจื” ืžืงื‘ื™ืœื”

ื”ืฆื•ืžืช Parallel Seq Scan ืžื™ื™ืฆืจ ืฉื•ืจื•ืช ืœืฆื‘ื™ืจื” ื—ืœืงื™ืช. ื”ืฆื•ืžืช "ืžืฆื˜ื‘ืจ ื—ืœืงื™" ื—ื•ืชืš ืงื•ื•ื™ื ืืœื” ื‘ืืžืฆืขื•ืช SUM(). ื‘ืกื•ืฃ, ืžื•ื ื” ื”-SUM ืžื›ืœ ืชื”ืœื™ืš ืขื•ื‘ื“ ื ืืกืฃ ืขืœ ื™ื“ื™ ื”ืฆื•ืžืช "Gather".

ื”ืชื•ืฆืื” ื”ืกื•ืคื™ืช ืžื—ื•ืฉื‘ืช ืขืœ ื™ื“ื™ ื”ืฆื•ืžืช "Finalize Aggregate". ืื ื™ืฉ ืœืš ืคื•ื ืงืฆื™ื•ืช ืฆื‘ื™ืจื” ืžืฉืœืš, ืืœ ืชืฉื›ื— ืœืกืžืŸ ืื•ืชืŸ ื›"ื‘ื˜ื•ื—ื•ืช ื‘ืžืงื‘ื™ืœ".

ืžืกืคืจ ืชื”ืœื™ื›ื™ ืขื•ื‘ื“

ื ื™ืชืŸ ืœื”ื’ื“ื™ืœ ืืช ืžืกืคืจ ืชื”ืœื™ื›ื™ ื”ืขื‘ื•ื“ื” ืžื‘ืœื™ ืœื”ืคืขื™ืœ ืžื—ื“ืฉ ืืช ื”ืฉืจืช:

explain analyze select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=1589702.14..1589702.15 rows=1 width=32) (actual time=8553.365..8553.365 rows=1 loops=1)
-> Gather (cost=1589701.91..1589702.12 rows=2 width=32) (actual time=8553.241..8555.067 rows=3 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (cost=1588701.91..1588701.92 rows=1 width=32) (actual time=8547.546..8547.546 rows=1 loops=3)
-> Parallel Seq Scan on lineitem (cost=0.00..1527393.33 rows=24523431 width=5) (actual time=0.038..5998.417 rows=19613238 loops=3)
Filter: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)
Rows Removed by Filter: 382112
Planning Time: 0.241 ms
Execution Time: 8555.131 ms

ืžื” ืงื•ืจื” ืคื”? ื”ื™ื• ืคื™ 2 ื™ื•ืชืจ ืชื”ืœื™ื›ื™ ืขื‘ื•ื“ื”, ื•ื”ื‘ืงืฉื” ื”ืคื›ื” ืœืžื”ื™ืจื” ืจืง ืคื™ 1,6599. ื”ื—ื™ืฉื•ื‘ื™ื ืžืขื ื™ื™ื ื™ื. ื”ื™ื• ืœื ื• 2 ืชื”ืœื™ื›ื™ ืขื•ื‘ื“ ื•ืžื ื”ื™ื’ ืื—ื“. ืœืื—ืจ ื”ืฉื™ื ื•ื™ ื–ื” ื”ืคืš ืœ-1+4.

ื”ืžื”ื™ืจื•ืช ื”ืžืงืกื™ืžืœื™ืช ืฉืœื ื• ืžืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ: 5/3 = 1,66(6) ืคืขืžื™ื.

ืื™ืš ื–ื” ืขื•ื‘ื“?

ะŸั€ะพั†ะตััั‹

ื‘ื™ืฆื•ืข ื”ื‘ืงืฉื” ืžืชื—ื™ืœ ืชืžื™ื“ ื‘ืชื”ืœื™ืš ื”ืžื•ื‘ื™ืœ. ื”ืžื ื”ื™ื’ ืขื•ืฉื” ื”ื›ืœ ืœื ืžืงื‘ื™ืœ ื•ืื™ื–ื” ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ. ืชื”ืœื™ื›ื™ื ืื—ืจื™ื ืฉืžื‘ืฆืขื™ื ืืช ืื•ืชืŸ ื‘ืงืฉื•ืช ื ืงืจืื™ื ืชื”ืœื™ื›ื™ ืขื•ื‘ื“. ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ ืžืฉืชืžืฉ ื‘ืชืฉืชื™ืช ืชื”ืœื™ื›ื™ ืขื‘ื•ื“ื” ื“ื™ื ืžื™ื™ื ื‘ืจืงืข (ืžื’ืจืกื” 9.4). ืžื›ื™ื•ื•ืŸ ืฉื—ืœืงื™ื ืื—ืจื™ื ืฉืœ PostgreSQL ืžืฉืชืžืฉื™ื ื‘ืชื”ืœื™ื›ื™ื ื•ืœื ื‘ืฉืจืฉื•ืจื™ื, ืฉืื™ืœืชื” ืขื 3 ืชื”ืœื™ื›ื™ ืขื‘ื•ื“ื” ื™ื›ื•ืœื” ืœื”ื™ื•ืช ืžื”ื™ืจื” ืคื™ 4 ืžืขื™ื‘ื•ื“ ืžืกื•ืจืชื™.

ืื™ื ื˜ืจืืงืฆื™ื”

ืชื”ืœื™ื›ื™ ืขื•ื‘ื“ ืžืชืงืฉืจื™ื ืขื ื”ืžื ื”ื™ื’ ื‘ืืžืฆืขื•ืช ืชื•ืจ ื”ื•ื“ืขื•ืช (ืžื‘ื•ืกืก ืขืœ ื–ื™ื›ืจื•ืŸ ืžืฉื•ืชืฃ). ืœื›ืœ ืชื”ืœื™ืš ื™ืฉ 2 ืชื•ืจื™ื: ืขื‘ื•ืจ ืฉื’ื™ืื•ืช ื•ืขื‘ื•ืจ tuples.

ื›ืžื” ื–ืจื™ืžื•ืช ืขื‘ื•ื“ื” ื ื“ืจืฉื•ืช?

ื”ื’ื‘ื•ืœ ื”ืžื™ื ื™ืžืœื™ ืžื•ื’ื“ืจ ืขืœ ื™ื“ื™ ื”ืคืจืžื˜ืจ max_parallel_workers_per_gather. ืœืื—ืจ ืžื›ืŸ, ืจืฅ ื”ื‘ืงืฉื•ืช ืœื•ืงื— ืชื”ืœื™ื›ื™ ืขื•ื‘ื“ ืžื”ืžืื’ืจ ื”ืžื•ื’ื‘ืœื™ื ืขืœ ื™ื“ื™ ื”ืคืจืžื˜ืจ max_parallel_workers size. ื”ืžื’ื‘ืœื” ื”ืื—ืจื•ื ื” ื”ื™ื max_worker_processes, ื›ืœื•ืžืจ, ื”ืžืกืคืจ ื”ื›ื•ืœืœ ืฉืœ ืชื”ืœื™ื›ื™ ืจืงืข.

ืื ืœื ื ื™ืชืŸ ื”ื™ื” ืœื”ืงืฆื•ืช ืชื”ืœื™ืš ืขื•ื‘ื“, ื”ืขื™ื‘ื•ื“ ื™ื”ื™ื” ื—ื“-ืชื”ืœื™ืš.

ืžืชื›ื ืŸ ื”ืฉืื™ืœืชื•ืช ื™ื›ื•ืœ ืœืฆืžืฆื ื–ืจื™ืžื•ืช ืขื‘ื•ื“ื” ื‘ื”ืชืื ืœื’ื•ื“ืœ ื”ื˜ื‘ืœื” ืื• ื”ืื™ื ื“ืงืก. ื™ืฉ ืคืจืžื˜ืจื™ื ืœื–ื” min_parallel_table_scan_size ะธ min_parallel_index_scan_size.

set min_parallel_table_scan_size='8MB'
8MB table => 1 worker
24MB table => 2 workers
72MB table => 3 workers
x => log(x / min_parallel_table_scan_size) / log(3) + 1 worker

ื‘ื›ืœ ืคืขื ืฉื”ืฉื•ืœื—ืŸ ื’ื“ื•ืœ ืคื™ 3 ืž min_parallel_(index|table)_scan_size, Postgres ืžื•ืกื™ืฃ ืชื”ืœื™ืš ืขื•ื‘ื“. ืžืกืคืจ ื–ืจื™ืžื•ืช ื”ืขื‘ื•ื“ื” ืื™ื ื• ืžื‘ื•ืกืก ืขืœ ืขืœื•ื™ื•ืช. ืชืœื•ืช ืžืขื’ืœื™ืช ืžืงืฉื” ืขืœ ื™ื™ืฉื•ืžื™ื ืžื•ืจื›ื‘ื™ื. ื‘ืžืงื•ื ื–ืืช, ื”ืžืชื›ื ืŸ ืžืฉืชืžืฉ ื‘ื›ืœืœื™ื ืคืฉื•ื˜ื™ื.

ื‘ืคื•ืขืœ, ื›ืœืœื™ื ืืœื• ืœื ืชืžื™ื“ ืžืชืื™ืžื™ื ืœื™ื™ืฆื•ืจ, ื›ืš ืฉื ื™ืชืŸ ืœืฉื ื•ืช ืืช ืžืกืคืจ ืชื”ืœื™ื›ื™ ื”ืขื‘ื•ื“ื” ืขื‘ื•ืจ ื˜ื‘ืœื” ืกืคืฆื™ืคื™ืช: ALTER TABLE ... SET (parallel_workers = N).

ืžื“ื•ืข ืœื ื ืขืฉื” ืฉื™ืžื•ืฉ ื‘ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ?

ื‘ื ื•ืกืฃ ืœืจืฉื™ืžืช ื”ื”ื’ื‘ืœื•ืช ื”ืืจื•ื›ื”, ื™ืฉ ื’ื ื‘ื“ื™ืงื•ืช ืขืœื•ื™ื•ืช:

parallel_setup_cost - ืœื”ื™ืžื ืข ืžืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ ืฉืœ ื‘ืงืฉื•ืช ืงืฆืจื•ืช. ืคืจืžื˜ืจ ื–ื” ืžืขืจื™ืš ืืช ื–ืžืŸ ื”ื›ื ืช ื”ื–ื™ื›ืจื•ืŸ, ื”ืชื—ืœืช ื”ืชื”ืœื™ืš ื•ื”ื—ืœืคืช ื ืชื•ื ื™ื ืจืืฉื•ื ื™ืช.

parallel_tuple_cost: ื”ืชืงืฉื•ืจืช ื‘ื™ืŸ ื”ืžื ื”ื™ื’ ืœืขื•ื‘ื“ื™ื ื™ื›ื•ืœื” ืœื”ืชืขื›ื‘ ื‘ื™ื—ืก ืœืžืกืคืจ ื”ื˜ืคื•ืœื™ื ืžืชื”ืœื™ื›ื™ ื”ืขื‘ื•ื“ื”. ืคืจืžื˜ืจ ื–ื” ืžื—ืฉื‘ ืืช ืขืœื•ืช ื”ื—ืœืคืช ื”ื ืชื•ื ื™ื.

ืœื•ืœืื” ืžืงื•ื ื ืช ืžืฆื˜ืจืคืช

PostgreSQL 9.6+ ะผะพะถะตั‚ ะฒั‹ะฟะพะปะฝัั‚ัŒ ะฒะปะพะถะตะฝะฝั‹ะต ั†ะธะบะปั‹ ะฟะฐั€ะฐะปะปะตะปัŒะฝะพ โ€” ัั‚ะพ ะฟั€ะพัั‚ะฐั ะพะฟะตั€ะฐั†ะธั.

explain (costs off) select c_custkey, count(o_orderkey)
                from    customer left outer join orders on
                                c_custkey = o_custkey and o_comment not like '%special%deposits%'
                group by c_custkey;
                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Finalize GroupAggregate
   Group Key: customer.c_custkey
   ->  Gather Merge
         Workers Planned: 4
         ->  Partial GroupAggregate
               Group Key: customer.c_custkey
               ->  Nested Loop Left Join
                     ->  Parallel Index Only Scan using customer_pkey on customer
                     ->  Index Scan using idx_orders_custkey on orders
                           Index Cond: (customer.c_custkey = o_custkey)
                           Filter: ((o_comment)::text !~~ '%special%deposits%'::text)

ื”ืื™ืกื•ืฃ ืžืชืจื—ืฉ ื‘ืฉืœื‘ ื”ืื—ืจื•ืŸ, ืื– Nested Loop Left Join ื”ื•ื ืคืขื•ืœื” ืžืงื‘ื™ืœื”. Parallel Index Only Scan ื”ื•ืฆื’ ืจืง ื‘ื’ืจืกื” 10. ื–ื” ืขื•ื‘ื“ ื‘ื“ื•ืžื” ืœืกืจื™ืงื” ื˜ื•ืจื™ืช ืžืงื‘ื™ืœื”. ืžึทืฆึธื‘ c_custkey = o_custkey ืงื•ืจื ื”ื–ืžื ื” ืื—ืช ืœื›ืœ ืžื—ืจื•ื–ืช ืœืงื•ื—. ืื– ื–ื” ืœื ืžืงื‘ื™ืœ.

Hash Join

ื›ืœ ืชื”ืœื™ืš ืขื•ื‘ื“ ื™ื•ืฆืจ ื˜ื‘ืœืช hash ืžืฉืœื• ืขื“ ืœ-PostgreSQL 11. ื•ืื ื™ืฉ ื™ื•ืชืจ ืžืืจื‘ืขื” ืžื”ืชื”ืœื™ื›ื™ื ื”ืืœื”, ื”ื‘ื™ืฆื•ืขื™ื ืœื ื™ืฉืชืคืจื•. ื‘ื’ืจืกื” ื”ื—ื“ืฉื”, ื˜ื‘ืœืช ื”ื’ื™ื‘ื•ื‘ ืžืฉื•ืชืคืช. ื›ืœ ืชื”ืœื™ืš ืขื•ื‘ื“ ื™ื›ื•ืœ ืœื”ืฉืชืžืฉ ื‘-WORK_MEM ื›ื“ื™ ืœื™ืฆื•ืจ ื˜ื‘ืœืช ื’ื™ื‘ื•ื‘.

select
        l_shipmode,
        sum(case
                when o_orderpriority = '1-URGENT'
                        or o_orderpriority = '2-HIGH'
                        then 1
                else 0
        end) as high_line_count,
        sum(case
                when o_orderpriority <> '1-URGENT'
                        and o_orderpriority <> '2-HIGH'
                        then 1
                else 0
        end) as low_line_count
from
        orders,
        lineitem
where
        o_orderkey = l_orderkey
        and l_shipmode in ('MAIL', 'AIR')
        and l_commitdate < l_receiptdate
        and l_shipdate < l_commitdate
        and l_receiptdate >= date '1996-01-01'
        and l_receiptdate < date '1996-01-01' + interval '1' year
group by
        l_shipmode
order by
        l_shipmode
LIMIT 1;
                                                                                                                                    QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=1964755.66..1964961.44 rows=1 width=27) (actual time=7579.592..7922.997 rows=1 loops=1)
   ->  Finalize GroupAggregate  (cost=1964755.66..1966196.11 rows=7 width=27) (actual time=7579.590..7579.591 rows=1 loops=1)
         Group Key: lineitem.l_shipmode
         ->  Gather Merge  (cost=1964755.66..1966195.83 rows=28 width=27) (actual time=7559.593..7922.319 rows=6 loops=1)
               Workers Planned: 4
               Workers Launched: 4
               ->  Partial GroupAggregate  (cost=1963755.61..1965192.44 rows=7 width=27) (actual time=7548.103..7564.592 rows=2 loops=5)
                     Group Key: lineitem.l_shipmode
                     ->  Sort  (cost=1963755.61..1963935.20 rows=71838 width=27) (actual time=7530.280..7539.688 rows=62519 loops=5)
                           Sort Key: lineitem.l_shipmode
                           Sort Method: external merge  Disk: 2304kB
                           Worker 0:  Sort Method: external merge  Disk: 2064kB
                           Worker 1:  Sort Method: external merge  Disk: 2384kB
                           Worker 2:  Sort Method: external merge  Disk: 2264kB
                           Worker 3:  Sort Method: external merge  Disk: 2336kB
                           ->  Parallel Hash Join  (cost=382571.01..1957960.99 rows=71838 width=27) (actual time=7036.917..7499.692 rows=62519 loops=5)
                                 Hash Cond: (lineitem.l_orderkey = orders.o_orderkey)
                                 ->  Parallel Seq Scan on lineitem  (cost=0.00..1552386.40 rows=71838 width=19) (actual time=0.583..4901.063 rows=62519 loops=5)
                                       Filter: ((l_shipmode = ANY ('{MAIL,AIR}'::bpchar[])) AND (l_commitdate < l_receiptdate) AND (l_shipdate < l_commitdate) AND (l_receiptdate >= '1996-01-01'::date) AND (l_receiptdate < '1997-01-01 00:00:00'::timestamp without time zone))
                                       Rows Removed by Filter: 11934691
                                 ->  Parallel Hash  (cost=313722.45..313722.45 rows=3750045 width=20) (actual time=2011.518..2011.518 rows=3000000 loops=5)
                                       Buckets: 65536  Batches: 256  Memory Usage: 3840kB
                                       ->  Parallel Seq Scan on orders  (cost=0.00..313722.45 rows=3750045 width=20) (actual time=0.029..995.948 rows=3000000 loops=5)
 Planning Time: 0.977 ms
 Execution Time: 7923.770 ms

ืฉืื™ืœืชื” 12 ืž-TPC-H ืžืฆื™ื’ื” ื‘ื‘ื™ืจื•ืจ ื—ื™ื‘ื•ืจ ื’ื™ื‘ื•ื‘ ืžืงื‘ื™ืœ. ื›ืœ ืชื”ืœื™ืš ืขื•ื‘ื“ ืชื•ืจื ืœื™ืฆื™ืจืช ื˜ื‘ืœืช ื’ื™ื‘ื•ื‘ ืžืฉื•ืชืคืช.

ืžื™ื–ื•ื’ ื”ืฆื˜ืจืฃ

ืฆื™ืจื•ืฃ ืžื™ื–ื•ื’ ืื™ื ื• ืžืงื‘ื™ืœ ื‘ืื•ืคื™ื•. ืืœ ืชื“ืื’ ืื ื–ื” ื”ืฉืœื‘ ื”ืื—ืจื•ืŸ ืฉืœ ื”ืฉืื™ืœืชื” - ื”ื™ื ืขื“ื™ื™ืŸ ื™ื›ื•ืœื” ืœืคืขื•ืœ ื‘ืžืงื‘ื™ืœ.

-- Query 2 from TPC-H
explain (costs off) select s_acctbal, s_name, n_name, p_partkey, p_mfgr, s_address, s_phone, s_comment
from    part, supplier, partsupp, nation, region
where
        p_partkey = ps_partkey
        and s_suppkey = ps_suppkey
        and p_size = 36
        and p_type like '%BRASS'
        and s_nationkey = n_nationkey
        and n_regionkey = r_regionkey
        and r_name = 'AMERICA'
        and ps_supplycost = (
                select
                        min(ps_supplycost)
                from    partsupp, supplier, nation, region
                where
                        p_partkey = ps_partkey
                        and s_suppkey = ps_suppkey
                        and s_nationkey = n_nationkey
                        and n_regionkey = r_regionkey
                        and r_name = 'AMERICA'
        )
order by s_acctbal desc, n_name, s_name, p_partkey
LIMIT 100;
                                                QUERY PLAN
----------------------------------------------------------------------------------------------------------
 Limit
   ->  Sort
         Sort Key: supplier.s_acctbal DESC, nation.n_name, supplier.s_name, part.p_partkey
         ->  Merge Join
               Merge Cond: (part.p_partkey = partsupp.ps_partkey)
               Join Filter: (partsupp.ps_supplycost = (SubPlan 1))
               ->  Gather Merge
                     Workers Planned: 4
                     ->  Parallel Index Scan using <strong>part_pkey</strong> on part
                           Filter: (((p_type)::text ~~ '%BRASS'::text) AND (p_size = 36))
               ->  Materialize
                     ->  Sort
                           Sort Key: partsupp.ps_partkey
                           ->  Nested Loop
                                 ->  Nested Loop
                                       Join Filter: (nation.n_regionkey = region.r_regionkey)
                                       ->  Seq Scan on region
                                             Filter: (r_name = 'AMERICA'::bpchar)
                                       ->  Hash Join
                                             Hash Cond: (supplier.s_nationkey = nation.n_nationkey)
                                             ->  Seq Scan on supplier
                                             ->  Hash
                                                   ->  Seq Scan on nation
                                 ->  Index Scan using idx_partsupp_suppkey on partsupp
                                       Index Cond: (ps_suppkey = supplier.s_suppkey)
               SubPlan 1
                 ->  Aggregate
                       ->  Nested Loop
                             Join Filter: (nation_1.n_regionkey = region_1.r_regionkey)
                             ->  Seq Scan on region region_1
                                   Filter: (r_name = 'AMERICA'::bpchar)
                             ->  Nested Loop
                                   ->  Nested Loop
                                         ->  Index Scan using idx_partsupp_partkey on partsupp partsupp_1
                                               Index Cond: (part.p_partkey = ps_partkey)
                                         ->  Index Scan using supplier_pkey on supplier supplier_1
                                               Index Cond: (s_suppkey = partsupp_1.ps_suppkey)
                                   ->  Index Scan using nation_pkey on nation nation_1
                                         Index Cond: (n_nationkey = supplier_1.s_nationkey)

ื”ืฆื•ืžืช "Merge Join" ืžืžื•ืงื ืžืขืœ "Gather Merge". ืื– ืžื™ื–ื•ื’ ืื™ื ื• ืžืฉืชืžืฉ ื‘ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ. ืื‘ืœ ื”ืฆื•ืžืช "ืกืจื™ืงืช ืื™ื ื“ืงืก ืžืงื‘ื™ืœื”" ืขื“ื™ื™ืŸ ืขื•ื–ืจ ืขื ื”ืงื˜ืข part_pkey.

ื—ื™ื‘ื•ืจ ืœืคื™ ืžืงื˜ืขื™ื

ื‘-PostgreSQL 11 ื—ื™ื‘ื•ืจ ืœืคื™ ืงื˜ืขื™ื ืžื•ืฉื‘ืช ื›ื‘ืจื™ืจืช ืžื—ื“ืœ: ื™ืฉ ืœื• ืชื–ืžื•ืŸ ื™ืงืจ ืžืื•ื“. ื ื™ืชืŸ ืœื—ื‘ืจ ื˜ื‘ืœืื•ืช ืขื ื—ืœื•ืงื” ื“ื•ืžื” ืœืžื—ื™ืฆื” ืื—ืจ ืžื—ื™ืฆื”. ื‘ื“ืจืš ื–ื• Postgres ื™ืฉืชืžืฉ ื‘ื˜ื‘ืœืื•ืช Hash ืงื˜ื ื•ืช ื™ื•ืชืจ. ื›ืœ ื—ื™ื‘ื•ืจ ืฉืœ ืงื˜ืขื™ื ื™ื›ื•ืœ ืœื”ื™ื•ืช ืžืงื‘ื™ืœ.

tpch=# set enable_partitionwise_join=t;
tpch=# explain (costs off) select * from prt1 t1, prt2 t2
where t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000;
                    QUERY PLAN
---------------------------------------------------
 Append
   ->  Hash Join
         Hash Cond: (t2.b = t1.a)
         ->  Seq Scan on prt2_p1 t2
               Filter: ((b >= 0) AND (b <= 10000))
         ->  Hash
               ->  Seq Scan on prt1_p1 t1
                     Filter: (b = 0)
   ->  Hash Join
         Hash Cond: (t2_1.b = t1_1.a)
         ->  Seq Scan on prt2_p2 t2_1
               Filter: ((b >= 0) AND (b <= 10000))
         ->  Hash
               ->  Seq Scan on prt1_p2 t1_1
                     Filter: (b = 0)
tpch=# set parallel_setup_cost = 1;
tpch=# set parallel_tuple_cost = 0.01;
tpch=# explain (costs off) select * from prt1 t1, prt2 t2
where t1.a = t2.b and t1.b = 0 and t2.b between 0 and 10000;
                        QUERY PLAN
-----------------------------------------------------------
 Gather
   Workers Planned: 4
   ->  Parallel Append
         ->  Parallel Hash Join
               Hash Cond: (t2_1.b = t1_1.a)
               ->  Parallel Seq Scan on prt2_p2 t2_1
                     Filter: ((b >= 0) AND (b <= 10000))
               ->  Parallel Hash
                     ->  Parallel Seq Scan on prt1_p2 t1_1
                           Filter: (b = 0)
         ->  Parallel Hash Join
               Hash Cond: (t2.b = t1.a)
               ->  Parallel Seq Scan on prt2_p1 t2
                     Filter: ((b >= 0) AND (b <= 10000))
               ->  Parallel Hash
                     ->  Parallel Seq Scan on prt1_p1 t1
                           Filter: (b = 0)

ื”ืขื™ืงืจ ืฉื”ื—ื™ื‘ื•ืจ ื‘ืงื˜ืขื™ื ื”ื•ื ืžืงื‘ื™ืœ ืจืง ืื ื”ืงื˜ืขื™ื ื”ืืœื” ื’ื“ื•ืœื™ื ืžืกืคื™ืง.

ืฆืจืฃ ืžืงื‘ื™ืœ

ืฆืจืฃ ืžืงื‘ื™ืœ ื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘ืžืงื•ื ื‘ืœื•ืงื™ื ืฉื•ื ื™ื ื‘ื–ืจื™ืžื•ืช ืขื‘ื•ื“ื” ืฉื•ื ื•ืช. ื–ื” ืงื•ืจื” ื‘ื“ืจืš ื›ืœืœ ืขื ืฉืื™ืœืชื•ืช UNION ALL. ื”ื—ื™ืกืจื•ืŸ ื”ื•ื ืคื—ื•ืช ืžืงื‘ื™ืœื™ื•ืช, ืžื›ื™ื•ื•ืŸ ืฉื›ืœ ืชื”ืœื™ืš ืขื•ื‘ื“ ืžืขื‘ื“ ืจืง ื‘ืงืฉื” ืื—ืช.

ืคื•ืขืœื™ื ื›ืืŸ 2 ืชื”ืœื™ื›ื™ ืขื‘ื•ื“ื”, ืื ื›ื™ 4 ืžื•ืคืขืœื™ื.

tpch=# explain (costs off) select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '1998-12-01' - interval '105' day union all select sum(l_quantity) as sum_qty from lineitem where l_shipdate <= date '2000-12-01' - interval '105' day;
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Gather
   Workers Planned: 2
   ->  Parallel Append
         ->  Aggregate
               ->  Seq Scan on lineitem
                     Filter: (l_shipdate <= '2000-08-18 00:00:00'::timestamp without time zone)
         ->  Aggregate
               ->  Seq Scan on lineitem lineitem_1
                     Filter: (l_shipdate <= '1998-08-18 00:00:00'::timestamp without time zone)

ื”ืžืฉืชื ื™ื ื”ื—ืฉื•ื‘ื™ื ื‘ื™ื•ืชืจ

  • WORK_MEM ืžื’ื‘ื™ืœ ื–ื™ื›ืจื•ืŸ ืœื›ืœ ืชื”ืœื™ืš, ืœื ืจืง ืฉืื™ืœืชื•ืช: work_mem ืชื”ืœื™ื›ื™ื ื—ื™ื‘ื•ืจื™ื = ื”ืจื‘ื” ื–ื™ื›ืจื•ืŸ.
  • max_parallel_workers_per_gather - ื‘ื›ืžื” ืชื”ืœื™ื›ื™ ืขื•ื‘ื“ ืชืฉืชืžืฉ ื”ืชื•ื›ื ื™ืช ื”ืžื‘ืฆืขืช ืœืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ ืžื”ืชื•ื›ื ื™ืช.
  • max_worker_processes - ืžืชืื™ื ืืช ื”ืžืกืคืจ ื”ื›ื•ืœืœ ืฉืœ ืชื”ืœื™ื›ื™ ืขื‘ื•ื“ื” ืœืžืกืคืจ ืœื™ื‘ื•ืช ื”ืžืขื‘ื“ ื‘ืฉืจืช.
  • max_parallel_workers - ื–ื”ื”, ืื‘ืœ ืœืชื”ืœื™ื›ื™ ืขื‘ื•ื“ื” ืžืงื‘ื™ืœื™ื.

ืชื•ืฆืื•ืช ืฉืœ

ื”ื—ืœ ืžื’ืจืกื” 9.6, ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ ื™ื›ื•ืœ ืœืฉืคืจ ืžืื•ื“ ืืช ื”ื‘ื™ืฆื•ืขื™ื ืฉืœ ืฉืื™ืœืชื•ืช ืžื•ืจื›ื‘ื•ืช ื”ืกื•ืจืงื•ืช ืฉื•ืจื•ืช ืื• ืื™ื ื“ืงืกื™ื ืจื‘ื™ื. ื‘-PostgreSQL 10, ืขื™ื‘ื•ื“ ืžืงื‘ื™ืœ ืžื•ืคืขืœ ื›ื‘ืจื™ืจืช ืžื—ื“ืœ. ื–ื›ื•ืจ ืœื”ืฉื‘ื™ืช ืื•ืชื• ื‘ืฉืจืชื™ื ืขื ืขื•ืžืก ืขื‘ื•ื“ื” OLTP ื’ื“ื•ืœ. ืกืจื™ืงื•ืช ืจืฆื™ืคื•ืช ืื• ืกืจื™ืงื•ืช ืื™ื ื“ืงืก ืฆื•ืจื›ื•ืช ืžืฉืื‘ื™ื ืจื‘ื™ื. ืื ืื™ื ืš ืžืคืขื™ืœ ื“ื•ื— ืขืœ ืžืขืจืš ื”ื ืชื•ื ื™ื ื›ื•ืœื•, ืชื•ื›ืœ ืœืฉืคืจ ืืช ื‘ื™ืฆื•ืขื™ ื”ืฉืื™ืœืชื•ืช ืขืœ ื™ื“ื™ ื”ื•ืกืคืช ืื™ื ื“ืงืกื™ื ื—ืกืจื™ื ืื• ืฉื™ืžื•ืฉ ื‘ื—ืœื•ืงื” ืžืชืื™ืžื”.

ืชื–ื›ื•ืจ

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”