在执行distinct、group、join是由多个列参数时,如果投影列里没有Hash分布列,则会选择一个列做hash动态重分布。GBase 8a数据库集群通过_t_gcluster_distinct_multi_redist和_t_gcluster_hash_redistribute_groupby_on_multiple_expression参数,可以指定多个列参与hash计算,避免GBase 8a做distinct、group、join时通过指定多列动态Hash重分布避免严重数据倾斜导致的性能问题。
目录导航
常见业务场景如下
select distinct id1,id2,id3... from table,
select id1,id2,count(*) from table group by id1,id2
select a.* from a left join b on a.id=b.id and a.id2=b.id2
当然,如果有hash列参与,或者第一个列没有比较严重的倾斜,那么这个参数就不要用了。注意倾斜的定义,可以参考执行时间,如果每次都是其它节点很快结束,某一个或几个节点耗时长,比如超过30%。 查看gnode计算节点上sql的方法,请参考 GBase 8a数据库集群运维巡检维护常用命令简化版
提醒
本文介绍的2个参数,都是下划线开头的,代表参数并没有正式对外开放使用,也不保证新版本还会保留,就算保留也不确保是相同的名字。所以如果在某个版本可用,后续升级时请咨询厂家获得变动列表并进行测试。
distinct 操作
参数
默认值为1,可选值为1和2。
_t_gcluster_distinct_multi_redist
参数为1,按照单列计算进行重分布去重
参数为2,按照多列计算进行重分布去重
说明
常量、标量子查询不会作为重分布列。
设置该参数为2,由于在hash阶段计算了多个列,随着distinct列的增多,计算成本增加,时间增长,所以要以测试结果为准,在数据倾斜和性能之间平衡。
另外,distinct,group等有多个列的场景,无论什么情况,都建议将唯一值多的放第一个。
样例
表结构
如下是一个2个列的随机分布表,没有hash分布列。
gbase> desc t1;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(100) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
2 rows in set (Elapsed: 00:00:00.00)
gbase> show create table t1;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| t1 | CREATE TABLE "t1" (
"id" int(11) DEFAULT NULL,
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
默认的执行计划
可以看到REDIST重分布时,用ID列。
gbase> explain select distinct id,name from t1;
+----+--------------+-----------+---------+-----------+
| ID | MOTION | OPERATION | TABLE | CONDITION |
+----+--------------+-----------+---------+-----------+
| 01 | [RESULT] | Step | <00> | |
| | | AGG | | |
| 00 | [REDIST(id)] | Table | t1[DIS] | |
| | | AGG | | |
+----+--------------+-----------+---------+-----------+
4 rows in set (Elapsed: 00:00:00.00)
参数后的执行计划
修改参数等于2后,重分布REDIST采用了id,name两个列。
gbase> set _t_gcluster_distinct_multi_redist=2;
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase> explain select distinct id,name from t1;
+----+-------------------+-----------+---------+-----------+
| ID | MOTION | OPERATION | TABLE | CONDITION |
+----+-------------------+-----------+---------+-----------+
| 01 | [RESULT] | Step | <00> | |
| | | AGG | | |
| 00 | [REDIST(id,name)] | Table | t1[DIS] | |
| | | AGG | | |
+----+-------------------+-----------+---------+-----------+
4 rows in set (Elapsed: 00:00:00.00)
group操作
参数
默认值是0,使用1个列做重分布。1是用多个列做重分布。
_t_gcluster_hash_redistribute_groupby_on_multiple_expression
说明
常量、标量子查询不会作为重分布列。
设置该参数为1,由于在hash阶段计算了多个列,随着group列的增多,计算成本增加,时间增长,所以要以测试结果为准,在数据倾斜和性能之间平衡。
另外,distinct,group等有多个列的场景,无论什么情况,都建议将唯一值多的放第一个。
样例
表结构
如下是一个2个列的随机分布表,没有hash分布列。
gbase> desc t1;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(100) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
2 rows in set (Elapsed: 00:00:00.00)
gbase> show create table t1;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| t1 | CREATE TABLE "t1" (
"id" int(11) DEFAULT NULL,
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
默认的执行计划
可以看到REDIST重分布时,用ID列。
gbase> explain select id,name,count(*) from t1 group by id,name;
+----+--------------+-----------+---------+-------------------+
| ID | MOTION | OPERATION | TABLE | CONDITION |
+----+--------------+-----------+---------+-------------------+
| 01 | [RESULT] | Step | <00> | |
| | | GROUP | | GROUP BY id, name |
| 00 | [REDIST(id)] | Table | t1[DIS] | |
| | | GROUP | | GROUP BY id, name |
+----+--------------+-----------+---------+-------------------+
4 rows in set (Elapsed: 00:00:00.01)
参数后的执行计划
修改参数等于1后,重分布REDIST采用了id,name两个列。
gbase> set _t_gcluster_hash_redistribute_groupby_on_multiple_expression=1;
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase> explain select id,name,count(*) from t1 group by id,name;
+----+-------------------+-----------+---------+-------------------+
| ID | MOTION | OPERATION | TABLE | CONDITION |
+----+-------------------+-----------+---------+-------------------+
| 01 | [RESULT] | Step | <00> | |
| | | GROUP | | GROUP BY id, name |
| 00 | [REDIST(id,name)] | Table | t1[DIS] | |
| | | GROUP | | GROUP BY id, name |
+----+-------------------+-----------+---------+-------------------+
4 rows in set (Elapsed: 00:00:00.00)
join 操作
参数
默认值为1,可选值为1和2。
_t_gcluster_join_multi_hash_optimize_level=
参数为1,按照单列计算进行hash重分布
参数为2,按照多列计算进行重分布
说明
常量、标量子查询不会作为重分布列。
设置该参数为2,由于在hash阶段计算了多个列,随着hash条件列的增多,计算成本增加,时间增长,所以要以测试结果为准,在数据倾斜和性能之间平衡。
样例
表结构
如下是2个随机分布表,没有hash分布列。
gbase> desc t1;
+-------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| name | varchar(100) | YES | | NULL | |
+-------+--------------+------+-----+---------+-------+
2 rows in set (Elapsed: 00:00:00.00)
gbase> show create table t1;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| t1 | CREATE TABLE "t1" (
"id" int(11) DEFAULT NULL,
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> show create table t3;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| t3 | CREATE TABLE "t3" (
"id" int(11) DEFAULT NULL,
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
默认的执行计划
可以看到REDIST重分布时,用了name列。
gbase> explain select t1.id,t1.name from t1 left join t3 on t1.id=t3.id and t1.name=t3.name;
+----+----------------+------------+---------+-----------------------------+
| ID | MOTION | OPERATION | TABLE | CONDITION |
+----+----------------+------------+---------+-----------------------------+
| 02 | [RESULT] | LEFT JOIN | | (id = id) AND (name = name) |
| | | Step | <00> | |
| | | Step | <01> | |
| 01 | [REDIST(name)] | Table | t3[DIS] | |
| 00 | [REDIST(name)] | Table | t1[DIS] | |
+----+----------------+------------+---------+-----------------------------+
5 rows in set (Elapsed: 00:00:00.00)
参数后的执行计划
修改参数等于2后,重分布REDIST采用了id,name两个列。
gbase> set _t_gcluster_join_multi_hash_optimize_level=2;
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase> explain select t1.id,t1.name from t1 left join t3 on t1.id=t3.id and t1.name=t3.name;
+----+-------------------+------------+---------+-----------------------------+
| ID | MOTION | OPERATION | TABLE | CONDITION |
+----+-------------------+------------+---------+-----------------------------+
| 02 | [RESULT] | LEFT JOIN | | (id = id) AND (name = name) |
| | | Step | <00> | |
| | | Step | <01> | |
| 01 | [REDIST(id,name)] | Table | t3[DIS] | |
| 00 | [REDIST(id,name)] | Table | t1[DIS] | |
+----+-------------------+------------+---------+-----------------------------+
5 rows in set (Elapsed: 00:00:00.01)
注意
有时一个表会拉成复制表,不会参与重分布。
gbase> explain select t1.id,t1.name from t1 left join t3 on t1.id=t3.id and t1.name=t3.name;
+----+-------------+------------+---------+-----------------------------+
| ID | MOTION | OPERATION | TABLE | CONDITION |
+----+-------------+------------+---------+-----------------------------+
| 01 | [RESULT] | LEFT JOIN | | (id = id) AND (name = name) |
| | | Table | t1[DIS] | |
| | | Step | <00> | |
| 00 | [BROADCAST] | Table | t3[DIS] | |
+----+-------------+------------+---------+-----------------------------+
4 rows in set (Elapsed: 00:00:00.00)
需要通过如下参数进行调优,但也许拉复制表性能更好呢?还是以测试结果为准。
gcluster_hash_redistribute_join_optimize=1
该参数详情参考 GBase 8a性能优化案例,大量小表join时拉了复制表导致只有1个节点运行,gcluster_hash_redistribute_join_optimize 参数调整