初识GBase 8a MPP Cluster 对分布式执行计划的理解
说明: 学习了三周GBase 8a MPP Cluster 相关知识,前段时间参加了孙云吉老师的 GBase 8a MPP Cluster explain 分布式执行计划相关的培训,个人知识有限,只记录下本人对 GBase 8a MPP Cluster 分布式执行计划浅显的理解 ( 截图来自于培训内容 ) 。
一 多表连接操作
二 group by 操作
三 优化案例举例
一 多 表连接操作
1 静态 hash join 执行计划

场景: 两个 hash 分布表进行 join, 并且关联列是 hash 分布列。
理论上静态hash join 是最优的,因为可以 直接在各个节点上分别单独执行分布式join 算子,不需要拉复制表或 hash 动态重分布。
但是当hash 分布列数据分布严重不均时,既数据倾斜严重,大多数数据集中在某一节点上,效率也会有影响,因为分布式运算时长取决于最慢的节点。
举例说明:
create table t1 (aid int,gid int) distributed by ('gid');
create table t2 (bid int,gid int) distributed by ('gid');
insert into t1 values(1,100),(1,200),(2,100),(3,50),(2,20),(6,80),(9,10),(6,0),(3,12),(1,18),(9,1);
insert into t2 values(1,0),(300,12),(1,6),(20,50),(50,10),(1,80),(3,10),(9,15),(20,12),(13,18),(2,1);
gbase> explain select t1.aid,t2.bid from t1 inner join t2 on t1.gid=t2.gid and t1.gid=100;

2 分布表join 复制表执行计划

场景: 随机分布表或HASH 分布表和复制表进行关联。
也可以直接在各个节点上分别单独执行分布式join 算子,不需要拉复制表或 hash 动态重分布。随机分布表和复制表进行关联时数据是被均匀打散到各个节点上的,效率较高。 HASH 分布表和复制表进行关联效率和 hash 分布列数据均匀程度有关。
举例说明:
create table t3(bid int,gid int) replicated;
insert into t3 select * from t2;
explain select t1.aid,t3.bid from t1 inner join t3 on t1.gid=t3.gid;

3 小表拉复制表join 执行计划

场景: t1,t2 两个 hash 分布表进行关联 , 其中 t2 关联列不是 hash 分布列,当 t2 数据量较小时,可以基于 t2 表在各个节点上生成一个 t2 的复制表,然后 t1 和 t2 的复制表进行关联,既将执行计划变成分布表 join 复制表。
或者 t1 随机分布表, t2 hash 分布表或随机分布表,其中 t2 数据量很小,可以基于 t2 表在各个节点上生成一个 t2 的复制表,然后 t1 和 t2 的复制表进行关联,既将执行计划变成分布表 join 复制表。
那么到底t2 数据量多小才适合拉复制表而不是选择动态重分布呢?有对应的参数进行限制。
举例说明:
create table t4 (aid int,gid int);
create table t5 (bid int,gid int);
insert into t4 values(1,100),(1,200),(2,100),(3,50),(2,20),(6,80),(9,10),(6,0),(3,12),(1,18),(9,1);
insert into t4 select * from t4;
insert into t4 select * from t4;
insert into t4 select * from t4;
insert into t4 select * from t4;
insert into t4 select * from t4;
insert into t5 values(1,0),(300,12),(1,6),(20,50),(50,10),(1,80),(3,10),(9,15),(20,12),(13,18),(2,1);
gbase> show variables like '%hash_redist_threshold_row%';

gbase> explain select t4.aid,t5.bid from t4 inner join t5 on t4.gid=t5.gid;

4 动态重分布hash join 执行计划
场景: t1,t2 两个 hash 分布表进行关联 , 其中 t2 关联列不是 hash 分布列, T2 表按照关联列做 hash 重分布,再和 T1 表在各节点分布式 join 。

t1,t2 两个 hash 分布表进行关联 , 其中关联列都不是 t1,t2 的 hash 分布列, T1 表和 T2 表都按照 gid 做 hash 重分布,重分布后再分布式各节点执行 join

举例说明:
t6 按照 gid 做 hash 重分布,在和 t2 表关联。
create table t6 (bid int,gid int) distributed by ('bid');
insert into t6 select * from t2;
gbase> explain select t1.aid,t6.bid from t1 inner join t6 on t1.gid=t6.gid;

T 6 表和T 7 表都按照gid 做 hash 重分布,重分布后再分布式各节点执行 join
create table t7 (aid int,gid int) distributed by ('aid');
insert into t7 select * from t1;
gbase> explain select t7.aid,t6.bid from t7 inner join t6 on t7.gid=t6.gid;

二 表group by 操作几种场景
1 静态hash group by 执行计划
场景: hash 分布表在执行 group by 操作时,分组的列中包含 hash 分布列,不需要动态重分布和二阶段 group by ,各个节点分布在本地进行 group by 后汇总结果集即可,效率最高。

举例说明:
gbase> create table t0 (c1 int,c2 varchar(10),c3 int,c4 int) distributed by ('c1');
gbase> insert into t0
values(1,'a',100,1),(2,'b',1,1),(100,'a',12,1),(1,'c',16,1),(3,'d',22,1),(4,'e',1,1),(1,'a',8,1),(200,'e',16,1),(200,'abc',12,1),(8,'x',12,1);
gbase> explain Select c1,c2,c3,sum(c4) from t0 where c3<100 group by 1,2,3;

2 动态重分布group by 执行计划
场景: hash 分布表在执行 group by 操作时,分组的列中不包含 hash 分布列,需要动态重分布或二阶段 group by ,效率较低,具体执行动态重分布还是二阶段 group by 受参数 gcluster_hash_redistribute_groupby_optimize 控制。 动态重分布是将group by 后的第一列作为 hash 分布列,动态生成 hash 分布表,在进行 group by 操作。

举例说明:
gbase> explain Select c2,c3,sum(c4) from t0 where c3<100 group by c2,c3;

gbase> explain Select c2,c3,sum(c4) from t0 where c3<100 group by c3,c2;

3 两阶段group by 执行计划
场景: hash 分布表在执行 group by 操作时,分组的列中不包含 hash 分布列,需要动态重分布或二阶段 group by ,效率较低,具体执行动态重分布还是二阶段 group by 受参数 gcluster_hash_redistribute_groupby_optimize 控制。在进行两阶段 group by 时,各个节点分别进行 group by 操作,将结果集汇总到集群层,在集群层在进行一次 group by 操作,汇总后结果集越大,性能越差。

举例说明:
gbase> show variables like 'gcluster_hash_redistribute_groupby_optimize';

gbase> set gcluster_hash_redistribute_groupby_optimize=0;
gbase> explain Select c2,c3,sum(c4) from t0 where c3<100 group by c2,c3;

三 优化案例举例

原 SQL :
gbase> select distinct a.class_name,b.sno,b.sname
-> from t_class a,t_student b,t_sex c,sc_course d
-> where a.class_id = b.class_id
-> and c.sex_id = b.sex_id
-> and b.sno = d.sno
-> and d.grade < 60;


通过执行计划可知:
1 t_sex拉复制表
2 t_class拉复制表
3 sc_course通过grade 条件 过滤后拉复制表
4 t_student随机分布表和t_sex复制表通过sex_id=sex_id进行关联
5 上一步结果集和t_class复制表通过glass_id进行关联
6 上一步结果集和sc_course复制表进行关联通过sno进行关联
7 上一步结果集合根据class_name列进行动态重分布 ( 因为有 distinct 操作 )
8 上一步结果集 合并 返回 给 客户端
优化:
当前存在的问题:
上述所有表都属于随机分布表,在多表关联时,数据库自动将 t_sex 、 t_class 、过滤后的 sc_course 拉复制表,和 t_student 随机分布表进行关联,其中 sc_course 即使通过条件过滤后数据量还是比较大,拉复制表的代价很大。
解决方案:
t_sex 、 t_class 表数据量很小,由随机分布表改成复制表。
sc_course 和 t_student 数据量较大,由随机分布表改成 HASH 分布表,分布列为关联列。
优化后:
gbase> select distinct a.class_name,b.sno,b.sname from t_class01 a,t_student01 b,t_sex01 c,sc_course01 d where a.class_id = b.class_id and c.sex_id = b.sex_id and b.sno = d.sno and d.grade < 60;

SQL 执行时间由 1.03 秒优化到 0.09 秒

1 sc_course01表 为 hash 分布表 ,sno 为 hash 列, 通过grade< 60 条件 过滤扫描。
2 t_student01表 为 hash 分布表 , sno 为 hash 列,全表扫描。
3 sc_course01 和 t_student01 通过 sno 列进行关联。
4 上一步结果集和 t_class01 表通过class_id 进行关联。
5 上一步结果集和 t_sex01 表通过 sex_id 进行关联。
6 合并结果集,返回到客户端。
欢迎关注我的微信公众号"IT小Chen",共同学习,共同成长!!!