VastbaseG100

基于openGauss内核开发的企业级关系型数据库。

Menu

配置并行查询

SMP并行技术是一种利用计算机多核CPU架构来实现多线程并行计算,以充分利用CPU资源来提高查询性能的技术。在复杂查询场景中,单个查询的执行较长,系统并发度低,通过SMP并行执行技术实现算子级的并行,能够有效减少查询执行时间,提升查询性能及资源利用率。SMP并行技术的整体实现思想是对于能够并行的查询算子,将数据分片,启动若干个工作线程分别计算,最后将结果汇总,返回前端。SMP并行执行增加数据交互算子(Stream),实现多个工作线程之间的数据交互,确保查询的正确性,完成整体的查询。

适用场景与限制

SMP特性通过算子并行来提升性能,同时会占用更多的系统资源,包括CPU、内存、I/O等等。本质上SMP是一种以资源换取时间的方式,在合适的场景以及资源充足的情况下,能够起到较好的性能提升效果;但是如果在不合适的场景下,或者资源不足的情况下,反而可能引起性能的劣化。SMP特性适用于分析类查询场景,这类场景的特点是单个查询时间较长,业务并发度低。通过SMP并行技术能够降低查询时延,提高系统吞吐性能。然而在事务类大并发业务场景下,由于单个查询本身的时延很短,使用多线程并行技术反而会增加查询时延,降低系统吞吐性能。

适用场景

  • 支持并行的算子:计划中存在以下算子支持并行。

    • Scan:支持行存普通表和行存分区表顺序扫描、列存普通表和列存分区表顺序扫描。
    • Join:HashJoin、NestLoop
    • Agg:HashAgg、SortAgg、PlainAgg、WindowAgg(只支持partition by,不支持order by)。
    • Stream:Local Redistribute、Local Broadcast
    • 其他:Result、Subqueryscan、Unique、Material、Setop、Append、VectoRow
  • SMP特有算子:为了实现并行,新增了并行线程间的数据交换Stream算子供SMP特性使用。这些新增的算子可以看做Stream算子的子类。

    • Local Gather:实现实例内部并行线程的数据汇总。
    • Local Redistribute:在实例内部各线程之间,按照分布键进行数据重分布。
    • Local Broadcast:将数据广播到实例内部的每个线程。
    • Local RoundRobin:在实例内部各线程之间实现数据轮询分发。
  • 示例说明,以TPCH Q1的并行计划为例。

    在这个计划中,实现了Scan以及HashAgg算子的并行,并新增了Local Gather数据交换算子。其中3号算子为Local Gather算子,上面标有的“dop: 1/4”表明该算子的发送端线程的并行度为4,而接受端线程的并行度为1,即下层的4号HashAggregate算子按照4并行度执行,而上层的1~2号算子按照串行执行,3号算子实现了实例内并行线程的数据汇总。

    通过计划Stream算子上表明的dop信息即可看出各个算子的并行情况。

非适用场景

  • 索引扫描不支持并行执行。
  • MergeJoin不支持并行执行。
  • WindowAgg order by不支持并行执行。
  • cursor不支持并行执行。
  • 存储过程和函数内的查询不支持并行执行。
  • 不支持子查询subplan和initplan的并行,以及包含子查询的算子的并行。
  • 查询语句中带有median操作的查询不支持并行执行。
  • 带全局临时表的查询不支持并行执行。
  • 物化视图的更新不支持并行执行。
  • UPDATE、DELETE、MERGE INTO语句不支持并行执行。
  • 含有伪列 ROWNUM 的语句不支持并行执行。
  • 使用自治事务的场景不支持并行执行。
  • FLASHBACK闪回场景不支持并行执行。
  • INSERT INTO时含有RAW类型的场景不支持并行执行。
  • 含有 number 类型聚集函数 avg 和 sum 的SQL语句不支持并行执行。

资源对SMP性能的影响

SMP架构是一种利用富余资源来换取时间的方案,计划并行之后必定会引起资源消耗的增加,包括CPU、内存、I/O等资源的消耗都会出现明显的增长,而且随着并行度的增大,资源消耗也随之增大。当上述资源成为瓶颈的情况下,SMP无法提升性能,反而可能导致数据库实例整体性能的劣化。下面对各种资源对SMP性能的影响情况分别进行说明。

  • CPU资源

    在一般客户场景中,系统CPU利用率不高的情况下,利用SMP并行架构能够更充分地利用系统CPU资源,提升系统性能。但当数据库服务器的CPU核数较少,CPU利用率已经比较高的情况下,如果打开SMP并行,不仅性能提升不明显,反而可能因为多线程间的资源竞争而导致性能劣化。

  • 内存资源

    查询并行后会导致内存使用量的增长,但每个算子使用内存上限仍受到work_mem等参数的限制。假设work_mem为4GB,并行度为2,那么每个并行线程所分到的内存上限为2GB。在work_mem较小或者系统内存不充裕的情况下,使用SMP并行后,可能出现数据下盘,导致查询性能劣化的问题。

  • I/O资源

    要实现并行扫描必定会增加I/O的资源消耗,因此只有在I/O资源充足的情况下,并行扫描才能够提高扫描性能。

其他因素对SMP性能的影响

除了资源因素外,还有一些因素也会对SMP并行性能造成影响。例如分区表中分区数据不均,以及系统并发度等因素。

  • 数据倾斜对SMP性能的影响

    当数据中存在严重数据倾斜时,并行效果较差。例如某表join列上某个值的数据量远大于其他值,开启并行后,根据join列的值对该表数据做hash重分布,使得某个并行线程的数据量远多于其他线程,造成长尾问题,导致并行后效果差。

  • 系统并发度对SMP性能的影响

    SMP特性会增加资源的使用,而在高并发场景下资源剩余较少。所以,如果在高并发场景下,开启SMP并行,会导致各查询之间严重的资源竞争问题。一旦出现了资源竞争的现象,无论是CPU、I/O、内存,都会导致整体性能的下降。因此在高并发场景下,开启SMP往往不能达到性能提升的效果,甚至可能引起性能劣化。

使用限制

想要利用SMP提升查询性能需要满足以下条件:

系统的CPU、内存、I/O和网络带宽等资源充足。SMP架构是一种利用富余资源来换取时间的方案,计划并行之后必定会引起资源消耗的增加,当上述资源成为瓶颈的情况下,SMP无法提升性能,反而可能导致性能的劣化。在出现资源瓶颈的情况下,建议关闭SMP。

配置步骤

1、观察当前系统负载情况,如果系统资源充足(资源利用率小于50%)则继续执行,否则退出。

2、设置query_dop=1(默认值),利用explain打出执行计划,观察计划是否符合适用场景与限制中的适用场景。如果符合,进入3。

3、设置query_dop=value,不考虑资源情况和计划特征,强制选取dop为1或value。

4、在符合条件的查询语句执行前设置合适的query_dop值,在语句执行结束后关闭query_dop。举例如下。

SET query_dop = 4;
SELECT COUNT(*) FROM t1 GROUP BY a;
SET query_dop = 1;
  • SMP并行框架允许的最大的线程参数数量可通过GUC参数max_stream_workers进行配置,默认值为64。

  • GUC参数enable_streaming_plan_degrade可配置允许SMP并行计划在线程资源不足的情况下退化为串行的开关,以保证query可以在SMP线程不足的情况下可以继续执行下去,默认是on。

  • 资源许可的情况下,并行度越高,性能提升效果越好。

  • SMP并行度支持会话级设置,推荐用户在执行符合要求的查询前,打开smp,执行结束后,关闭smp。以免在业务峰值时,对业务造成冲击。

示例

示例1: 并行顺序查询。

1、创建测试表test并插入数据。

create table test(id int,name text,age int);
insert into test values(generate_series(1,1000000),'vastbase',34);

2、指定分析表test。

analyze test;

3、设置并行度为5并打印并行查询计划。

set query_dop=5;
explain select count(*) from test where id<100;

返回结果如下:

                                      QUERY PLAN                                 
     
---------------------------------------------------------------------------------
 Aggregate  (cost=8100.25..8100.26 rows=1 width=8)
   ->  Streaming(type: LOCAL GATHER dop: 1/5)  (cost=8100.25..8100.26 rows=1 widt
h=8)
         ->  Aggregate  (cost=8100.25..8100.26 rows=1 width=8)
               ->  Seq Scan on test  (cost=0.00..8100.00 rows=100 width=0)
                     Filter: (id < 100)
(5 rows)

4、设置并行度为2并打印并行查询计划。

set query_dop=2;
explain select count(*) from test where id<100;

返回结果如下:

                                       QUERY PLAN
----------------------------------------------------------------------------------------
 Aggregate  (cost=11250.25..11250.26 rows=1 width=8)
   ->  Streaming(type: LOCAL GATHER dop: 1/2)  (cost=11250.25..11250.26 rows=1 width=8)
         ->  Aggregate  (cost=11250.25..11250.26 rows=1 width=8)
               ->  Seq Scan on test  (cost=0.00..11250.00 rows=100 width=0)
                     Filter: (id < 100)
(5 rows)

示例2: 并行嵌套查询。

1、创建测试表并插入数据。

create table test_a (id int, col varchar(20));
create table test_b (id int, col int);
create table t3(id int, id2 int, id3 int);
insert into test_a  values(generate_series(1,200000), 'xiaohong');
insert into test_b  values(generate_series(100,200000), 20);
insert into t3 values(generate_series(1, 200000), generate_series(1, 100), generate_series(1, 10));

2、分析表。

analyze test_a;
analyze test_b;
analyze t3;

3、设置并行度为2并打印嵌套循环并行查询计划。

set query_dop=2;
explain (costs off) select col from test_a where id in (select id from test_b where id in (select id from t3));

返回结果为:

                                    QUERY PLAN
----------------------------------------------------------------------------------
 Streaming(type: LOCAL GATHER dop: 1/2)
   ->  Nested Loop Semi Join
         Join Filter: (test_a.id = test_b.id)
         ->  Streaming(type: LOCAL REDISTRIBUTE dop: 2/2)
               ->  Seq Scan on test_a
         ->  Materialize
               ->  Streaming(type: LOCAL REDISTRIBUTE dop: 2/2)
                     ->  Nested Loop Semi Join
                           Join Filter: (test_b.id = t3.id)
                           ->  Streaming(type: LOCAL REDISTRIBUTE dop: 2/2)
                                 ->  Seq Scan on test_b
                           ->  Materialize
                                 ->  Streaming(type: LOCAL REDISTRIBUTE dop: 2/2)
                                       ->  Seq Scan on t3
(14 rows)

4、设置并行度为5并打印嵌套循环并行查询计划。

set query_dop=3;
explain (costs off) select col from test_a where id in (select id from test_b where id in (select id from t3));

返回结果为:

                                    QUERY PLAN
----------------------------------------------------------------------------------
 Streaming(type: LOCAL GATHER dop: 1/3)
   ->  Nested Loop Semi Join
         Join Filter: (test_a.id = test_b.id)
         ->  Streaming(type: LOCAL REDISTRIBUTE dop: 3/3)
               ->  Seq Scan on test_a
         ->  Materialize
               ->  Streaming(type: LOCAL REDISTRIBUTE dop: 3/3)
                     ->  Nested Loop Semi Join
                           Join Filter: (test_b.id = t3.id)
                           ->  Streaming(type: LOCAL REDISTRIBUTE dop: 3/3)
                                 ->  Seq Scan on test_b
                           ->  Materialize
                                 ->  Streaming(type: LOCAL REDISTRIBUTE dop: 3/3)
                                       ->  Seq Scan on t3
(14 rows)

示例3: 并行哈希连接。

1、创建测试表并插入数据。

create table parallel_hashjoin_test_a (id int);
create table parallel_hashjoin_test_b (id int);
insert into parallel_hashjoin_test_a select n from generate_series(1,1000) n;
insert into parallel_hashjoin_test_b select n from generate_series(1,1000000) n;

2、收集两张表的统计信息。

analyse parallel_hashjoin_test_a;
analyse parallel_hashjoin_test_b;

3、设置并行度为5并打印并行查询计划。

set query_dop=5;
explain (costs off) select * from parallel_hashjoin_test_a full join parallel_hashjoin_test_b on parallel_hashjoin_test_a.id = parallel_hashjoin_test_b.id where parallel_hashjoin_test_a.id < 10 order by parallel_hashjoin_test_a.id;

返回结果如下:

                                      QUERY PLAN
--------------------------------------------------------------------------------------
 Sort
   Sort Key: parallel_hashjoin_test_a.id
   ->  Streaming(type: LOCAL GATHER dop: 1/5)
         ->  Hash Right Join
               Hash Cond: (parallel_hashjoin_test_b.id = parallel_hashjoin_test_a.id)
               ->  Streaming(type: LOCAL REDISTRIBUTE dop: 5/5)
                     ->  Seq Scan on parallel_hashjoin_test_b
               ->  Hash
                     ->  Streaming(type: LOCAL REDISTRIBUTE dop: 5/1)
                           ->  Seq Scan on parallel_hashjoin_test_a
                                 Filter: (id < 10)
(11 rows)

4、设置并行度为2并打印并行查询计划。

set query_dop=2;
explain (costs off) select * from parallel_hashjoin_test_a full join parallel_hashjoin_test_b on parallel_hashjoin_test_a.id = parallel_hashjoin_test_b.id where parallel_hashjoin_test_a.id < 10 order by parallel_hashjoin_test_a.id;

返回结果如下:

                                   QUERY PLAN
--------------------------------------------------------------------------------
 Sort
   Sort Key: parallel_hashjoin_test_a.id
   ->  Hash Right Join
         Hash Cond: (parallel_hashjoin_test_b.id = parallel_hashjoin_test_a.id)
         ->  Streaming(type: LOCAL GATHER dop: 1/2)
               ->  Seq Scan on parallel_hashjoin_test_b
         ->  Hash
               ->  Seq Scan on parallel_hashjoin_test_a
                     Filter: (id < 10)
(9 rows)