การสืบค้นแบบขนานใน PostgreSQL

การสืบค้นแบบขนานใน PostgreSQL
CPU สมัยใหม่มีคอร์จำนวนมาก หลายปีที่ผ่านมา แอปพลิเคชันได้ส่งข้อความค้นหาไปยังฐานข้อมูลแบบคู่ขนานกัน หากเป็นการสืบค้นรายงานบนหลายแถวในตาราง การสืบค้นจะทำงานเร็วขึ้นเมื่อใช้ CPU หลายตัว และ PostgreSQL สามารถทำได้ตั้งแต่เวอร์ชัน 9.6

การสืบค้นแบบขนานใช้เวลา 3 ปี เราต้องเขียนโค้ดใหม่ในขั้นตอนต่างๆ ของการดำเนินการสืบค้น PostgreSQL 9.6 เปิดตัวโครงสร้างพื้นฐานเพื่อปรับปรุงโค้ดเพิ่มเติม ในเวอร์ชันถัดๆ ไป แบบสอบถามประเภทอื่นๆ จะดำเนินการพร้อมกัน

ข้อ จำกัด

  • อย่าเปิดใช้งานการดำเนินการแบบขนานหากคอร์ทั้งหมดยุ่งอยู่แล้ว มิฉะนั้น คำขออื่นๆ จะทำให้ช้าลง
  • สิ่งสำคัญที่สุดคือการประมวลผลแบบขนานที่มีค่า WORK_MEM สูงจะใช้หน่วยความจำจำนวนมาก - การรวมแฮชหรือการเรียงลำดับแต่ละรายการจะใช้หน่วยความจำ work_mem
  • การสืบค้น OLTP ที่มีความหน่วงต่ำไม่สามารถเร่งความเร็วได้ด้วยการดำเนินการแบบขนาน และถ้าแบบสอบถามส่งคืนหนึ่งแถว การประมวลผลแบบขนานจะทำให้การค้นหาช้าลงเท่านั้น
  • นักพัฒนาชอบใช้เกณฑ์มาตรฐาน TPC-H บางทีคุณอาจมีคำถามที่คล้ายกันสำหรับการดำเนินการแบบขนานที่สมบูรณ์แบบ
  • เฉพาะแบบสอบถาม SELECT ที่ไม่มีการล็อกเพรดิเคตเท่านั้นที่จะดำเนินการแบบขนาน
  • บางครั้งการจัดทำดัชนีที่เหมาะสมจะดีกว่าการสแกนตารางตามลำดับในโหมดขนาน
  • ไม่สนับสนุนการหยุดคำสั่งและเคอร์เซอร์ชั่วคราว
  • ฟังก์ชันหน้าต่างและฟังก์ชันการรวมชุดที่เรียงลำดับไม่ขนานกัน
  • คุณจะไม่ได้รับสิ่งใดจากเวิร์กโหลด I/O
  • ไม่มีอัลกอริธึมการเรียงลำดับแบบขนาน แต่การสืบค้นที่มีการเรียงลำดับสามารถดำเนินการแบบคู่ขนานได้ในบางลักษณะ
  • แทนที่ CTE (WITH ...) ด้วย SELECT ที่ซ้อนกันเพื่อเปิดใช้งานการประมวลผลแบบขนาน
  • Wrapper ข้อมูลของบุคคลที่สามยังไม่รองรับการประมวลผลแบบขนาน (แต่ทำได้!)
  • ไม่รองรับการเข้าร่วมภายนอกแบบเต็ม
  • max_rows ปิดใช้งานการประมวลผลแบบขนาน
  • หากแบบสอบถามมีฟังก์ชันที่ไม่ได้ทำเครื่องหมายว่า PARALLEL SAFE จะเป็นเธรดเดี่ยว
  • ระดับการแยกธุรกรรมแบบอนุกรมจะปิดใช้งานการประมวลผลแบบขนาน

สภาพแวดล้อมการทดสอบ

นักพัฒนา 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 daemon ดังนั้น ประสิทธิภาพการสืบค้นจึงไม่ถูกจำกัดโดย I/O ของดิสก์ มันใช้รอบ CPU เพื่อ:

  • อ่านทีละแถวจากหน้าตาราง
  • เปรียบเทียบค่าสตริงและเงื่อนไข 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

การสแกนตามลำดับทำให้เกิดแถวมากเกินไปโดยไม่มีการรวมกลุ่ม ดังนั้นแบบสอบถามจึงดำเนินการโดยแกน CPU ตัวเดียว

ถ้าคุณเพิ่ม 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”

ผลลัพธ์สุดท้ายจะคำนวณโดยโหนด "สรุปผลรวม" หากคุณมีฟังก์ชันการรวมของคุณเอง อย่าลืมทำเครื่องหมายว่าเป็น "ปลอดภัยแบบขนาน"

จำนวนกระบวนการของผู้ปฏิบัติงาน

สามารถเพิ่มจำนวนกระบวนการของผู้ปฏิบัติงานได้โดยไม่ต้องรีสตาร์ทเซิร์ฟเวอร์:

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+1

การเร่งความเร็วสูงสุดของเราจากการประมวลผลแบบขนาน: 5/3 = 1,66(6) เท่า

มันทำงานอย่างไร

กระบวนการต่างๆ

การดำเนินการร้องขอจะเริ่มต้นด้วยกระบวนการนำเสมอ ผู้นำทำทุกอย่างที่ไม่ขนานกันและประมวลผลแบบขนานบ้าง กระบวนการอื่นที่ดำเนินการตามคำขอเดียวกันเรียกว่ากระบวนการของผู้ปฏิบัติงาน การประมวลผลแบบขนานใช้โครงสร้างพื้นฐาน กระบวนการทำงานเบื้องหลังแบบไดนามิก (จากเวอร์ชัน 9.4) เนื่องจากส่วนอื่นๆ ของ PostgreSQL ใช้กระบวนการมากกว่าเธรด การสืบค้นที่มีกระบวนการของผู้ปฏิบัติงาน 3 คนจึงอาจเร็วกว่าการประมวลผลแบบเดิมถึง 4 เท่า

ปฏิสัมพันธ์

กระบวนการของผู้ปฏิบัติงานสื่อสารกับผู้นำผ่านคิวข้อความ (ขึ้นอยู่กับหน่วยความจำที่ใช้ร่วมกัน) แต่ละกระบวนการมี 2 คิว: สำหรับข้อผิดพลาดและสำหรับสิ่งอันดับ

จำเป็นต้องมีเวิร์กโฟลว์จำนวนเท่าใด?

ขีดจำกัดขั้นต่ำถูกกำหนดโดยพารามิเตอร์ 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 อ่านหนึ่งคำสั่งต่อสตริงไคลเอนต์ มันจึงไม่ขนานกัน

แฮชเข้าร่วม

กระบวนการของผู้ปฏิบัติงานแต่ละกระบวนการจะสร้างตารางแฮชของตัวเองจนถึง 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)

โหนด "ผสานรวม" อยู่เหนือ "รวบรวมผสาน" ดังนั้นการรวมจึงไม่ใช้การประมวลผลแบบขนาน แต่โหนด "การสแกนดัชนีคู่ขนาน" ยังคงช่วยในส่วนนี้ได้ part_pkey.

การเชื่อมต่อตามส่วน

ใน PostgreSQL 11 การเชื่อมต่อตามส่วน ปิดใช้งานตามค่าเริ่มต้น: มีการกำหนดเวลาที่มีราคาแพงมาก ตารางที่มีการแบ่งพาร์ติชั่นที่คล้ายกันสามารถรวมพาร์ติชั่นทีละพาร์ติชั่นได้ วิธีนี้ Postgres จะใช้ตารางแฮชที่เล็กกว่า การเชื่อมต่อแต่ละส่วนสามารถขนานกันได้

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 ข้อเสียคือการมีความขนานน้อยกว่า เนื่องจากแต่ละกระบวนการของผู้ปฏิบัติงานจะประมวลผลคำขอเพียง 1 รายการเท่านั้น

มีกระบวนการของผู้ปฏิบัติงาน 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 — ปรับจำนวนกระบวนการของผู้ปฏิบัติงานทั้งหมดเป็นจำนวนคอร์ CPU บนเซิร์ฟเวอร์
  • max_parallel_workers - เหมือนกัน แต่สำหรับกระบวนการทำงานแบบขนาน

ผลของการ

ในเวอร์ชัน 9.6 การประมวลผลแบบขนานสามารถปรับปรุงประสิทธิภาพของการสืบค้นที่ซับซ้อนซึ่งสแกนแถวหรือดัชนีจำนวนมากได้อย่างมาก ใน PostgreSQL 10 การประมวลผลแบบขนานจะถูกเปิดใช้งานตามค่าเริ่มต้น อย่าลืมปิดการใช้งานบนเซิร์ฟเวอร์ที่มีปริมาณงาน OLTP ขนาดใหญ่ การสแกนตามลำดับหรือการสแกนดัชนีใช้ทรัพยากรจำนวนมาก หากคุณไม่ได้เรียกใช้รายงานเกี่ยวกับชุดข้อมูลทั้งหมด คุณสามารถปรับปรุงประสิทธิภาพการสืบค้นได้โดยการเพิ่มดัชนีที่ขาดหายไปหรือใช้การแบ่งพาร์ติชันที่เหมาะสม

การอ้างอิง

ที่มา: will.com

เพิ่มความคิดเห็น