3.7 使用 Plan Hint 进行调优
3.7.8 运行倾斜的 hint
功能描述
指明查询运行时重分布过程中存在倾斜的重分布键和倾斜值,针对Join和HashAgg运 算中的重分布进行优化。
语法格式
● 指定单表倾斜:
skew(table (column) [(value)])
● 指定中间结果倾斜:
skew((join_rel) (column) [(value)])
参数说明
● table表示存在倾斜的单个表名。
● join_rel表示参与join的两个或多个表,如(t1 t2)表示t1和t2join后的结果存在 倾斜。
● column表示倾斜表中存在倾斜的一个或多个列。
● value表示倾斜的列中存在倾斜的一个或多个值。
说明
● skew hint仅在需要重分布且指定的倾斜信息与查询执行过程中的重分布信息相匹配时才会被 使用。
● skew hint受GUC参数skew_option限制,如果参数处于关闭状态,则无法进行skew hint倾 斜调优。
● skew hint目前仅处理普通表和子查询类型的表关系,支持基表hint、子查询hint、with as子 句hint。对于子查询,无论提升与否都支持在skew hint中使用,这点与其它hint不一样。
● 对于倾斜表,如果定义了别名,则在hint中必须使用别名。
● 对于倾斜列,在不产生歧义的情况下,可以使用原名也可以使用别名。skew hint的column 不支持表达式,如果需要指定采用分布键为表达式的重分布存在倾斜,需要将重分布键指定 为新的列,以新的列进行hint。
● 对于倾斜值,个数需为列数的整数倍并按列的顺序进行组合,组合的个数不能超过10个。如 果各倾斜列的倾斜值的个数不一样,为了满足按列组合,值可以重复指定。如,表t1的c1和 c2存在倾斜,c1列的倾斜值只有a1,而c2列的倾斜有b1和b2,则skew hint如下:skew(t1 (c1 c2) ((a1 b1)(a1 b2)))。例中(a1 b1)为一个值组合,NULL可以作为倾斜值出现,每个 hint中的值组合不超过十个, 且需为列的整数倍。
● 在Join的重分布优化中,skew hint中的value不可缺省,在HashAgg中可以缺省。
● 对于表、列、值中若指定多个,则同类间需以空格分离。
● 对于倾斜值,不支持在hint中进行类型强转;对于string类型,需要使用单引号。
例如:
● 指定单表倾斜
每一个skew hint用来表示一个表关系存在的倾斜信息,如果想要指定在查询中的 多个表关系存在的倾斜信息,则通过指定多个skew hint实现。
在指定skew时,包括以下四个场景的用法:
– 单列单值: skew(t (c1) (v1))
说明:表关系t的c1列中的v1值在查询执行中存在倾斜。
– 单列多值:skew(t (c1) (v1 v2 v3 ...)) 间结果倾斜的skew hint来进行倾斜的调优。skew((t1 t2) (c1) (v1))
说明:表关系t1和t2 Join后的结果存在倾斜,倾斜的是t1表的c1列,c1列的倾斜
● 对于提升的子查询,skew hint支持直接使用子查询名进行hint。如果明确子查询 提升后的哪一个基表存在倾斜,则直接使用基表进行hint的可用性更高。
with customer_total_return as
(select sr_customer_sk as ctr_customer_sk ,sr_store_sk as ctr_store_sk
,sum(SR_FEE) as ctr_total_return from store_returns
,date_dim
where sr_returned_date_sk = d_date_sk and d_year =2000
group by sr_customer_sk ,sr_store_sk)
select c_customer_id
from customer_total_return ctr1 ,store
,customer
where ctr1.ctr_total_return > (select avg(ctr_total_return)*1.2
from customer_total_return ctr2
where ctr1.ctr_store_sk = ctr2.ctr_store_sk) and s_store_sk = ctr1.ctr_store_sk
and s_state = 'NM'
and ctr1.ctr_customer_sk = c_customer_sk order by c_customer_id
limit 100;
对内层with子句中的HashAgg和外层的Hash Join进行hint指定,带hint的查询如 下:
explain
with customer_total_return as
(select /*+ skew(store_returns(sr_store_sk sr_customer_sk)) */sr_customer_sk as ctr_customer_sk ,sr_store_sk as ctr_store_sk
,sum(SR_FEE) as ctr_total_return from store_returns
,date_dim
where sr_returned_date_sk = d_date_sk and d_year =2000
group by sr_customer_sk ,sr_store_sk)
select /*+ skew(ctr1(ctr_customer_sk)(11))*/ c_customer_id from customer_total_return ctr1
,store ,customer
where ctr1.ctr_total_return > (select avg(ctr_total_return)*1.2 from customer_total_return ctr2
where ctr1.ctr_store_sk = ctr2.ctr_store_sk) and s_store_sk = ctr1.ctr_store_sk
and s_state = 'NM'
and ctr1.ctr_customer_sk = c_customer_sk order by c_customer_id
limit 100;
该hint表示:内层with子句中的group by在做HashAgg中进行重分布时存在倾 斜,对应原计划的10和21号Hash Agg算子;外层ctr1表的ctr_customer_sk列在做 Hash Join中进行重分布时存在倾斜,对应原计划的6号算子。生成计划如下所 示:
从优化后的计划可以看出:①对于Hash Agg,由于其重分布存在倾斜,所以优化 为双层Agg;②对于Hash Join,同样由于其重分布存在倾斜,所以优化为采用新 的重分布算子。
● 需要改写query后进行hint 不带hint的查询和计划如下:
explain select count(*) from store_sales_1 group by round(ss_list_price);
由于hint中列不支持表达式,在进行倾斜优化时需要借助subquery改写查询,改 写后的查询和计划如下:
explain select count(*)
from (select round(ss_list_price),ss_hdemo_sk
from store_sales_1)tmp(a,ss_hdemo_sk) group by a;
改写注意不要影响到业务逻辑。
采用改写后的查询进行hint,带hint的查询和计划如下:
explain
select /*+ skew(tmp(a)) */ count(*)
from (select round(ss_list_price),ss_hdemo_sk
from store_sales_1)tmp(a,ss_hdemo_sk) group by a;
从计划可以看出,对Hash Agg进行倾斜优化后,采用了双层agg实现,大大过滤 了进行重分布时的数据量,减少了重分布时间。
此外,需要说明的是,对于子查询,支持使用查询内部的列进行hint,如:
explain
select /*+ skew(tmp(b)) */ count(*)
from (select round(ss_list_price) b,ss_hdemo_sk from store_sales_1)tmp(a,ss_hdemo_sk) group by a;