PolarDB PostgreSQL版(兼容Oracle)支持使用跨機(jī)并行查詢功能進(jìn)行分析型查詢,實現(xiàn)一定的HTAP能力。本文介紹如何使用跨機(jī)并行查詢,提升分析型查詢的性能。

原理介紹

當(dāng)一條查詢請求在查詢協(xié)調(diào)節(jié)點上被執(zhí)行跨機(jī)并行查詢時,該查詢產(chǎn)生的執(zhí)行計劃會被分片路由至各個執(zhí)行節(jié)點,每個執(zhí)行節(jié)點將會執(zhí)行各自的分片計劃,并將分片的查詢結(jié)果匯總至查詢協(xié)調(diào)節(jié)點。可以稱查詢協(xié)調(diào)節(jié)點為QC(Query Coordinator)節(jié)點,稱分片計劃的執(zhí)行節(jié)點為PX(Parallel Execution)節(jié)點。

跨機(jī)并行查詢原理

如上圖所示,RO1QC節(jié)點,它接收了一條查詢輸入請求,并將查詢計劃分片路由至RO2、RO3、RO4三個PX節(jié)點。每個PX節(jié)點將會針對各自接收到的分片計劃,最終從PolarFS共享存儲上讀取到各自所需的數(shù)據(jù)塊,由執(zhí)行節(jié)點執(zhí)行完成相應(yīng)的分片計劃,并將執(zhí)行結(jié)果返回為QC節(jié)點,QC節(jié)點匯總后返回查詢結(jié)果。

注意事項

由于跨機(jī)并行查詢功能需使用多個只讀節(jié)點資源,因此只適用于低頻次的分析型查詢。

細(xì)粒度使用方式

使用跨機(jī)并行查詢可以通過以下三種粒度進(jìn)行分析型查詢:
  • 系統(tǒng)粒度:通過參數(shù)控制所有session所有查詢是否開啟跨機(jī)并行查詢。
  • 會話粒度:通過alter sessionsession級別的GUC參數(shù)控制當(dāng)前session是否開啟跨機(jī)并行查詢。
  • 查詢粒度:通過hint指定具體查詢是否開啟跨機(jī)并行查詢。

參數(shù)說明

默認(rèn)情況下,PolarDB PostgreSQL版(兼容Oracle)不開啟跨機(jī)并行查詢功能。若您需要使用此功能,請使用如下參數(shù):

參數(shù)說明
polar_cluster_map用于查詢當(dāng)前PolarDB PostgreSQL版(兼容Oracle)所有只讀節(jié)點的名稱。該參數(shù)不可配置。當(dāng)您新增一個只讀節(jié)點時,該參數(shù)會進(jìn)行更新。
說明 只有內(nèi)核小版本(V1.1.20)(發(fā)布時間:20221月)之前創(chuàng)建的集群才包含該參數(shù)。
polar_px_nodes指定參與跨機(jī)并行查詢的只讀節(jié)點。默認(rèn)為空,表示所有只讀節(jié)點都參與。可配置為指定節(jié)點參與跨機(jī)并行查詢,以逗號分隔。例如:
SHOW polar_px_nodes ;
 polar_px_nodes
----------------

(1 row)
SET polar_px_nodes='node1,node2';
SHOW polar_px_nodes ;
 polar_px_nodes
----------------
 node1,node2
(1 row)
polar_px_enable_replay_waitPolarDB PostgreSQL版(兼容Oracle)的主節(jié)點與只讀節(jié)點存在一定程度的延遲,當(dāng)主節(jié)點執(zhí)行DDL語句(例如CREATE TABLE),只讀節(jié)點需要耗時回放該DDL日志后才可見新創(chuàng)建的表。當(dāng)設(shè)置polar_px_enable_replay_waiton后,跨機(jī)并行查詢啟用強(qiáng)一致性,當(dāng)前發(fā)起的跨機(jī)并行查詢請求路由到只讀節(jié)點上執(zhí)行時,需要只讀節(jié)點回放到該查詢請求前最近的一條日志后,才會執(zhí)行查詢請求。

該參數(shù)默認(rèn)為off,即關(guān)閉強(qiáng)一致性,在數(shù)據(jù)庫主備日志延遲較高時,不保證只讀節(jié)點可以讀到最近的DDL記錄。您可配置polar_px_enable_replay_waiton,表示啟用強(qiáng)一致性,但是跨機(jī)并行查詢會損失一定程度的性能。

該參數(shù)可以指定數(shù)據(jù)庫角色進(jìn)行開啟。

polar_px_max_workers_number設(shè)置單個節(jié)點上的最大跨機(jī)并行查詢workers進(jìn)程數(shù),默認(rèn)為30。該參數(shù)限制了單個節(jié)點上的最大并發(fā)度,節(jié)點上所有會話的跨機(jī)并行查詢workers進(jìn)程數(shù)不能超過該參數(shù)大小。
polar_enable_px指定是否開啟跨機(jī)并行查詢功能。默認(rèn)為off,即不開啟。
polar_px_dop_per_node設(shè)置當(dāng)前會話并行查詢的并行度,默認(rèn)為1,推薦值為當(dāng)前CPU總核數(shù)。若設(shè)置該參數(shù)為N,則一個會話在每個節(jié)點上將會啟用Npx workers進(jìn)程,用于處理當(dāng)前的跨機(jī)并行查詢邏輯。
px_workers指定跨機(jī)并行查詢是否對特定表生效。默認(rèn)不生效。跨機(jī)并行查詢功能比較消耗計算節(jié)點集群資源,因此只有對設(shè)置了px_workers的表才使用該功能。例如:
--表示t1表允許跨機(jī)并行查詢
ALTER TABLE t1 SET(px_workers=1);

--表示t1表禁止跨機(jī)并行查詢
ALTER TABLE t1 SET(px_workers=-1);

--表示t1表忽略跨機(jī)并行查詢, 默認(rèn)狀態(tài)
ALTER TABLE t1 SET(px_workers=0);
synchronous_commitWAL相關(guān)配置參數(shù),指定當(dāng)數(shù)據(jù)庫提交事務(wù)時是否需要等待WAL日志寫入硬盤后才向客戶端返回成功。取值如下:
  • off:不需要等待WAL日志寫入硬盤后才向客戶端返回成功。可以使用off0false或者no代替。
  • on:默認(rèn)值,需要等待WAL日志寫入硬盤后才向客戶端返回成功。可以使用ontrueyes或者1代替。
  • local:WAL記錄寫入并刷寫到本地磁盤。
  • remote_write:WAL記錄成功發(fā)送給備機(jī),遠(yuǎn)程備機(jī)確認(rèn)寫入。
  • remote_apply:直到備機(jī)接收到的事務(wù)回放,才返回主機(jī)確認(rèn)可以提交。
說明 PX下參數(shù)需要設(shè)置為on。

示例

本示例以簡單的單表查詢操作,來描述跨機(jī)并行查詢的功能是否有效。

示例背景:

執(zhí)行如下命令,創(chuàng)建test表并插入基礎(chǔ)數(shù)據(jù)。

CREATE TABLE test(id int);
INSERT INTO test SELECT generate_series(1,1000000);
EXPLAIN SELECT * FROM test;

默認(rèn)情況下跨機(jī)并行查詢功能是不開啟的,單表查詢執(zhí)行計劃為原生的Seq Scan,結(jié)果如下所示。

                       QUERY PLAN
--------------------------------------------------------
 Seq Scan on test  (cost=0.00..35.50 rows=2550 width=4)
(1 row)

通過以下步驟,開啟并使用跨機(jī)并行查詢功能:

  1. test表啟用跨機(jī)并行查詢功能。
    ALTER TABLE test SET (px_workers=1);
    SET polar_enable_px=on;
    EXPLAIN SELECT * FROM test;

    查詢結(jié)果如下:

                                      QUERY PLAN
    -------------------------------------------------------------------------------
     PX Coordinator 2:1  (slice1; segments: 2)  (cost=0.00..431.00 rows=1 width=4)
       ->  Seq Scan on test (scan partial)  (cost=0.00..431.00 rows=1 width=4)
     Optimizer: PolarDB PX Optimizer
    (3 rows)
  2. 查詢當(dāng)前所有只讀節(jié)點的名稱。

    查詢命令如下:

    SHOW polar_cluster_map;

    查詢結(jié)果如下:

     polar_cluster_map
    -------------------
     node1,node2,node3
    (1 row)

    可得出當(dāng)前集群有3個只讀節(jié)點,名稱分別為:node1,node2node3。

  3. 指定node1node2只讀節(jié)點參與跨機(jī)并行查詢。

    命令如下:

    SET polar_px_nodes='node1,node2';

    查詢參與并行查詢的節(jié)點:

    SHOW polar_px_nodes ;

    查詢結(jié)果如下:

     polar_px_nodes
    ----------------
     node1,node2
    (1 row)

性能數(shù)據(jù)

5個只讀節(jié)點的情況下,測試的性能數(shù)據(jù)如下:

  • SELECT COUNT(*)掃表的場景下,跨機(jī)并行查詢比單機(jī)并行查詢加速60倍。
  • TPC-H場景下,跨機(jī)并行查詢比單機(jī)并行查詢加速30倍。
    說明 此處的TPC-H場景是基于TPC-H的基準(zhǔn)測試,并不能與已發(fā)布的TPC-H基準(zhǔn)測試結(jié)果相比較,此處提及的測試結(jié)果并不符合TPC-H基準(zhǔn)測試的所有要求。

細(xì)粒度使用跨機(jī)并行查詢

各個粒度下如何使用跨機(jī)并行查詢進(jìn)行分析型查詢?nèi)缦滤荆?/p>

  • 系統(tǒng)粒度控制

    系統(tǒng)粒度控制主要通過設(shè)置全局guc參數(shù)控制開啟跨機(jī)并行查詢,指定并行度。

    示例
    postgres=# alter system set polar_enable_px=1;
    ALTER SYSTEM
    postgres=# alter system set polar_px_dop_per_node=1;
    ALTER SYSTEM
    postgres=# select pg_reload_conf();
     pg_reload_conf
    ----------------
     t
    (1 row)
    postgres=# \c postgres
    You are now connected to database "postgres" as user "postgres".
    
    postgres=# drop table if exists t1;
    DROP TABLE
    postgres=# select id into t1 from generate_series(1, 1000) as id order by id desc;
    SELECT 1000
    postgres=# alter table t1 set (px_workers=1);
    ALTER TABLE
    postgres=# explain (verbose, costs off)   select * from t1 where id < 10;
                    QUERY PLAN
    -------------------------------------------
     PX Coordinator 2:1  (slice1; segments: 2)
       Output: id
       ->  Partial Seq Scan on public.t1
             Output: id
             Filter: (t1.id < 10)
     Optimizer: PolarDB PX Optimizer
    (6 rows)
  • 會話粒度控制
    會話粒度控制可以通過ALTER SESSION語法,也可以通過session級別的GUC參數(shù)控制。
    • ALTER SESSION語法
      ALTER SESSION ENABLE PARALLEL QUERY
      ALTER SESSION DISABLE PARALLEL QUERY
      ALTER SESSION FORCE PARALLEL QUERY [PARALLEL integer]
      說明
      • ALTER SESSION ENABLE PARALLEL QUERY表示當(dāng)前session允許通過hint或者并行語法來開啟并行查詢。
      • ALTER SESSION DISABLE PARALLEL QUERY表示當(dāng)前session只能串行執(zhí)行查詢,不能并行查詢,指定hint或者并行語法也無效。
      • ALTER SESSION FORCE PARALLEL QUERY [PARALLEL integer]表示強(qiáng)制當(dāng)前session開啟并行執(zhí)行,并行度為PARALLEL integer,如果后者沒有指定,則使用數(shù)據(jù)庫默認(rèn)并行度polar_px_dop_per_node參數(shù)值。

        實際并行度優(yōu)先級為:hint指定 > FORCE PARALLEL指定 > polar_px_dop_per_node指定。

      ALTER SESSION命令會更改跨機(jī)并行查詢的配置參數(shù),僅影響當(dāng)前會話,會話重連后會重置,默認(rèn)值enable。

      示例
      --enable
      postgres=# set polar_enable_px = false;
      SET
      postgres=# set polar_px_enable_hint = true;
      SET
      postgres=# alter session enable parallel query;
      ALTER SESSION
      
      postgres=# explain (verbose, costs off)   select /*+ PARALLEL(4)*/ * from t1 where id < 10;
      INFO:  [HINTS] PX PARALLEL(4) accepted.
                      QUERY PLAN
      -------------------------------------------
       PX Coordinator 8:1  (slice1; segments: 8)
         Output: id
         ->  Partial Seq Scan on public.t1
               Output: id
               Filter: (t1.id < 10)
       Optimizer: PolarDB PX Optimizer
      (6 rows)
      --disable
      postgres=# set polar_enable_px = false;
      SET
      postgres=# set polar_px_enable_hint = true;
      SET
      postgres=# alter session disable parallel query;
      ALTER SESSION
      
      postgres=# explain (verbose, costs off)   select /*+ PARALLEL(4)*/ * from t1 where id < 10;
             QUERY PLAN
      ------------------------
       Seq Scan on public.t1
         Output: id
         Filter: (t1.id < 10)
      (3 rows)
      --force
      postgres=# set polar_enable_px = false;
      SET
      postgres=# set polar_px_enable_hint = false;
      SET
      postgres=# alter session force parallel query;
      ALTER SESSION
      
      postgres=# explain (verbose, costs off)   select * from t1 where id < 10;
                 QUERY PLAN
      -------------------------------------------
       PX Coordinator 2:1  (slice1; segments: 2)
         Output: id
         ->  Partial Seq Scan on public.t1
               Output: id
               Filter: (t1.id < 10)
       Optimizer: PolarDB PX Optimizer
      (6 rows)
      
      postgres=# alter session force parallel query parallel 2;
      ALTER SESSION
      postgres=# explain (verbose, costs off)   select * from t1 where id < 10;
                      QUERY PLAN
      -------------------------------------------
       PX Coordinator 4:1  (slice1; segments: 4)
         Output: id
         ->  Partial Seq Scan on public.t1
               Output: id
               Filter: (t1.id < 10)
       Optimizer: PolarDB PX Optimizer
      (6 rows)
    • GUC參數(shù)控制

      GUC參數(shù)本身具有系統(tǒng)粒度屬性和會話粒度屬性,因此也可以實現(xiàn)會話粒度控制。

      示例
      postgres=# set polar_enable_px = true;
      SET
      postgres=# set polar_px_dop_per_node = 1;
      SET
      postgres=# explain (verbose, costs off)   select * from t1 where id < 10;
                      QUERY PLAN
      -------------------------------------------
       PX Coordinator 2:1  (slice1; segments: 2)
         Output: id
         ->  Partial Seq Scan on public.t1
               Output: id
               Filter: (t1.id < 10)
       Optimizer: PolarDB PX Optimizer
      (6 rows)
  • 查詢粒度控制
    查詢粒度控制主要是通過sql hint指定當(dāng)前sql查詢是否開啟跨機(jī)并行,以及并行度設(shè)置。具體Hint語法如下所示:
    /*+ PARALLEL(DEFAULT) */
    /*+ PARALLEL(integer) */
    /*+ NO_PARALLEL(tablename) */
    說明
    • PARALLEL(DEFAULT)表示指定使用跨機(jī)并行查詢,采用系統(tǒng)默認(rèn)polar_px_dop_per_node并行度配置。
    • PARALLEL(integer)表示指定使用跨機(jī)并行查詢,采用指定的integer并行度配置。
    • NO_PARALLEL(tablename)表示指定表不能使用跨機(jī)并行查詢,整個查詢中如果包含這張表,則整個查詢也不能使用并行查詢。
    • Oracle兼容,當(dāng)parallel hint混用時,存在以下注意事項:
      • 多個hint塊時,/*+.A.*/ /*+.B.*/ /*+.C.*/,只有第一個hint塊生效。
      • 單個hint塊中多個parallel hint并用時,/*+ parallel(A) parallel(B)*/,如果ABdop值不相等,則ABhint作用沖突,所有parallel hint失效;如果AB值相等,則其中一個生效。
      • 單個hint塊中parallel/no_parallel hint并用時,/*+ parallel(A) no_parallel(t1)*/,則no_parallelhint失效。
    • 當(dāng)前并行查詢只支持parallel/no_parallel hint,暫不支持其他hint。
    • 查詢粒度的跨機(jī)并行查詢是否開啟,由GUC參數(shù)polar_px_enable_hint控制,默認(rèn)為false
    示例
    postgres=# set polar_enable_px = false;
    SET
    postgres=# set polar_px_dop_per_node = 1;
    SET
    postgres=# set polar_px_enable_hint = true;
    SET
    postgres=# explain (verbose, costs off)   select * from t1 where id < 10;
           QUERY PLAN
    ------------------------
     Seq Scan on public.t1
       Output: id
       Filter: (t1.id < 10)
    (3 rows)
    
    postgres=# explain (verbose, costs off)   select /*+ PARALLEL(DEFAULT) */ * from t1 where id < 10;
                    QUERY PLAN
    -------------------------------------------
     PX Coordinator 2:1  (slice1; segments: 2)
       Output: id
       ->  Partial Seq Scan on public.t1
             Output: id
             Filter: (t1.id < 10)
     Optimizer: PolarDB PX Optimizer
    (6 rows)
    
    postgres=# explain (verbose, costs off)   select /*+ PARALLEL(4) */ * from t1 where id < 10;
                    QUERY PLAN
    -------------------------------------------
     PX Coordinator 8:1  (slice1; segments: 8)
       Output: id
       ->  Partial Seq Scan on public.t1
             Output: id
             Filter: (t1.id < 10)
     Optimizer: PolarDB PX Optimizer
    (6 rows)
    
    postgres=# explain (verbose, costs off)   select /*+ PARALLEL(0) */ * from t1 where id < 10;
           QUERY PLAN
    ------------------------
     Seq Scan on public.t1
       Output: id
       Filter: (t1.id < 10)
    (3 rows)
    
    postgres=# explain (verbose, costs off)   select /*+ NO_PARALLEL(t1) */ * from t1 where id < 10;
           QUERY PLAN
    ------------------------
     Seq Scan on public.t1
       Output: id
       Filter: (t1.id < 10)
    (3 rows)
  • 各粒度組合效果
    三種粒度相互組合時,實際結(jié)果按照以下規(guī)則:
    系統(tǒng)粒度會話粒度查詢粒度實際結(jié)果
    polar_enable_px=onpolar_px_dop_per_node=Xenable不指定hint并行,并行度X
    polar_enable_px=onpolar_px_dop_per_node=XenablePARALLEL(Y)并行,并行度Y
    polar_enable_px=onpolar_px_dop_per_node=XenableNO_PARALLEL不并行
    polar_enable_px=onpolar_px_dop_per_node=Xdisable不指定hint不并行
    polar_enable_px=onpolar_px_dop_per_node=XdisablePARALLEL(Y)不并行
    polar_enable_px=onpolar_px_dop_per_node=XdisableNO_PARALLEL不并行
    polar_enable_px=onpolar_px_dop_per_node=XFORCE PARALLEL Z不指定hint并行,并行度Z
    polar_enable_px=onpolar_px_dop_per_node=XFORCE PARALLEL ZPARALLEL(Y)并行,并行度Y
    polar_enable_px=onpolar_px_dop_per_node=XFORCE PARALLEL ZNO_PARALLEL不并行
    polar_enable_px=offpolar_px_dop_per_node=Xenable不指定hint不并行
    polar_enable_px=offpolar_px_dop_per_node=XenablePARALLEL(Y)并行, 并行度Y
    polar_enable_px=offpolar_px_dop_per_node=XenableNO_PARALLEL不并行
    polar_enable_px=offpolar_px_dop_per_node=Xdisable不指定hint不并行
    polar_enable_px=offpolar_px_dop_per_node=XdisablePARALLEL(Y)不并行
    polar_enable_px=offpolar_px_dop_per_node=XdisableNO_PARALLEL不并行
    polar_enable_px=offpolar_px_dop_per_node=XFORCE PARALLEL Z不指定hint并行,并行度Z
    polar_enable_px=offpolar_px_dop_per_node=XFORCE PARALLEL ZPARALLEL(Y)并行,并行度Y
    polar_enable_px=offpolar_px_dop_per_node=XFORCE PARALLEL ZNO_PARALLEL不并行