Greenplum广播与重分布原理详解

Greenplum的执行计划和PostgreSQL基本一致,但是在表关联的时候还是会有区别,因为greenplum中数据不同于pg,greenplum是将数据分布在多个节点上。所以当两表关联时关联列又不是表的分布键时就会出现表的广播和重分布。本文将详细介绍广播和重分布的区别及场景。

在此之前,我们需要先了解下在greenplum中表之间分布式关联有哪些类型?
表的分布式关联主要分为两大类:
1、单库关联,即表的关联键和分布键一致,所以只需要在单个库中得到结果后再关联即可,因此也不会涉及到广播和重分布;
2、跨库关联,即表的关联键和分布键不一致,因此数据需要重新分布来达到单库关联的效果,这也是本文主要讲解的地方。

对于跨库关联又分为两种情况:
2.1、一张表的关联键是分布列,另一张表不是;
2.2、两张表的关联键均不是分布列。

详细情况如下图所示(a、b两表中c1列是分布列):
在这里插入图片描述

下面结合实例来讲解广播和重分布的原理及场景。
构建环境:

postgres=# CREATE table a(c1 int,c2 int)distributed by(c1);
CREATE TABLE
postgres=# CREATE table b(c1 int,c2 int)distributed by(c1);
CREATE TABLE

现在主要来看看跨库关联的两种情况:
–情况一:a.c1=b.c2
即a表关联键是分布列,b表不是。这种情况可以通过两种方法来实现表的关联:
1、重分布:将b表按照c2字段将数据重新分布到每个节点上,然后再和a表关联。这种情况下重分布的数据量就是b表的数据量N;
2、广播:将a表上的数据进行广播,即每个节点都放一份全量数据,然后再和b表关联。这种情况下数据量大小就是a表数据量M * 节点数L。

综上,我们可以得出结论:
N > M * L:选择广播;
N < =M * L:选择重分布。

下面用实例来验证下:
–M = 20000,N = 15000,L=2:

postgres=# insert into a select generate_series(1,20000),generate_series(1,20000);
INSERT 0 20000
postgres=# insert into b select generate_series(1,15000),generate_series(1,15000);
INSERT 0 15000

执行计划:重分布

postgres=# EXPLAIN select * from a,b where a.c1=b.c2;
                                               QUERY PLAN                                                
---------------------------------------------------------------------------------------------------------
 Gather Motion 2:1  (slice2; segments: 2)  (cost=655.50..1117.00 rows=15000 width=16)
   ->  Hash Join  (cost=655.50..1117.00 rows=7500 width=16)
         Hash Cond: a.c1 = b.c2
         ->  Seq Scan on a  (cost=0.00..224.00 rows=10000 width=8)
         ->  Hash  (cost=468.00..468.00 rows=7500 width=8)
               ->  Redistribute Motion 2:2  (slice1; segments: 2)  (cost=0.00..468.00 rows=7500 width=8)
                     Hash Key: b.c2
                     ->  Seq Scan on b  (cost=0.00..168.00 rows=7500 width=8)
 Optimizer status: legacy query optimizer
(9 rows)

–M = 1000,N = 15000,L=2:

postgres=# TRUNCATE a;
TRUNCATE TABLE
postgres=# insert into a select generate_series(1,1000),generate_series(1,1000);
INSERT 0 1000

执行计划:广播

postgres=# EXPLAIN select * from a,b where a.c1=b.c2;
                                             QUERY PLAN                                              
-----------------------------------------------------------------------------------------------------
 Gather Motion 2:1  (slice2; segments: 2)  (cost=67.00..287.50 rows=1000 width=16)
   ->  Hash Join  (cost=67.00..287.50 rows=500 width=16)
         Hash Cond: b.c2 = a.c1
         ->  Seq Scan on b  (cost=0.00..168.00 rows=7500 width=8)
         ->  Hash  (cost=42.00..42.00 rows=1000 width=8)
               ->  Broadcast Motion 2:2  (slice1; segments: 2)  (cost=0.00..42.00 rows=1000 width=8)
                     ->  Seq Scan on a  (cost=0.00..12.00 rows=500 width=8)
 Optimizer status: legacy query optimizer
(8 rows)

–情况二:a.c2=b.c2
即两表的关联键都不是分布键,这种情况也分两种方法:
1、重分布:将表a和表b均按照c2列,将数据重分布到每个节点上,所以代价就是表a数据量M + 表b数据量N;
2、广播:将数据量较少的表数据进行广播,因此代价就是min(M,N) * 节点数L。
综上,我们又可以得出此情况下的结论:
M + N > min(M,N) * L:广播;
M + N <= min(M,N) * L:重分布。

–M = 15000,N = 15000,L=2:

postgres=# insert into b select generate_series(1,15000),generate_series(1,15000);
INSERT 0 15000
postgres=# insert into a select generate_series(1,15000),generate_series(1,15000); 
INSERT 0 15000

执行计划:重分布

postgres=# EXPLAIN select * from a,b where a.c2 = b.c2;
                                               QUERY PLAN                                                
---------------------------------------------------------------------------------------------------------
 Gather Motion 2:1  (slice3; segments: 2)  (cost=655.50..1348.50 rows=15000 width=16)
   ->  Hash Join  (cost=655.50..1348.50 rows=7500 width=16)
         Hash Cond: a.c2 = b.c2
         ->  Redistribute Motion 2:2  (slice1; segments: 2)  (cost=0.00..468.00 rows=7500 width=8)
               Hash Key: a.c2
               ->  Seq Scan on a  (cost=0.00..168.00 rows=7500 width=8)
         ->  Hash  (cost=468.00..468.00 rows=7500 width=8)
               ->  Redistribute Motion 2:2  (slice2; segments: 2)  (cost=0.00..468.00 rows=7500 width=8)
                     Hash Key: b.c2
                     ->  Seq Scan on b  (cost=0.00..168.00 rows=7500 width=8)
 Optimizer status: legacy query optimizer
(11 rows)

–M = 1000,N = 15000,L=2:

postgres=# TRUNCATE a;
TRUNCATE TABLE
postgres=# insert into a select generate_series(1,1000),generate_series(1,1000);
INSERT 0 1000
postgres=# ANALYZE a;
ANALYZE

执行计划:广播

postgres=# EXPLAIN select * from a,b where a.c2 = b.c2;
                                             QUERY PLAN                                              
-----------------------------------------------------------------------------------------------------
 Gather Motion 2:1  (slice2; segments: 2)  (cost=67.00..287.50 rows=1000 width=16)
   ->  Hash Join  (cost=67.00..287.50 rows=500 width=16)
         Hash Cond: b.c2 = a.c2
         ->  Seq Scan on b  (cost=0.00..168.00 rows=7500 width=8)
         ->  Hash  (cost=42.00..42.00 rows=1000 width=8)
               ->  Broadcast Motion 2:2  (slice1; segments: 2)  (cost=0.00..42.00 rows=1000 width=8)
                     ->  Seq Scan on a  (cost=0.00..12.00 rows=500 width=8)
 Optimizer status: legacy query optimizer
(8 rows)

总结:

当表的分布式关联涉及到关联键不是分布列的时候,就会出现表的广播和重分布:
1、部分表的关联键不是分布列:N > M * L则使用广播,反之使用重分布;
2、所有表的关联键都不是分布列:M + N > min(M,N) * L则使用广播,反之使用重分布。

发布了161 篇原创文章 · 获赞 95 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_39540651/article/details/105688710