使用 spark sql extensions 实现 skew join

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lsshlsw/article/details/88412896

背景

使用 Join 时,如果出现数据倾斜就会导致OOM或者单task长时间执行的现象,如果是大小表关联的场景,还可以使用 MAPJOIN 的方式来解决,如果遇到两张大表可能就束手无策了。

尝试通过 spark sql extensions 自定义 skew hint 语法解决上述问题。

skew hint

实现方式

使用spark sql extensions 的扩展功能,增加自定义 hint 在 sql parser 层对相关逻辑进行处理,这种方式无需对spark源码进行修改,对比之前 aspect的实现方式 也更加优雅

原理

  1. 根据倾斜表和倾斜字段自动分析倾斜值,如果在sql中明确提供倾斜值则会跳过该过程。
    根据倾斜表和倾斜字段分析倾斜值
  2. 两表针对倾斜值(skew value) 进行分别过滤,数据倾斜侧对非倾斜表数据进行广播后进行 broadcast join,非数据倾斜侧直接进行 sort merge join,最后将结果进行 UNION,同时使用 COALESCE 减少碎片分区
    在这里插入图片描述

Cost

shuffle 网络 I/O 代价 executor 最大内存空间代价
hint skew join Size(t1) + Size(t2) + Size(sv2) * (n-1) - Size(sv1) (Size(t1)+Size(t2)) / p + Size(sv2)
sort merge join Size(t1) + Size(t2) Size(sv1) + Size(sv2)
  • t1: skew_table
  • t2: normal_table
  • sv1: skew_value
  • sv2: t2 表对应的 skew_value
  • n: executor 数量
  • p: shuffle 分区数
  • Size(sv2) 大多数情况都可以忽略不计

sort merge join 的方式,Size(sv1)在极端场景下内存占用可能非常高,会导致长尾任务或者OOM导致任务失败。

hint skew join 的方式每个 task 的负载非常均衡,可以简单的通过增加分区数量的方式提高并行度并且减少executor内存压力

在这里插入图片描述

用法

语法

提供表名,列名

-- 提供倾斜表名和列名
SELECT /*+ SKEW('skewTable', 'id') */ *
  FROM skewTable
  JOIN normalTable
  ON skewTable.id = normalTable.id

-- 如果使用了子查询
SELECT /*+ SKEW('t1', 'id') */ *
  FROM (select id,c1 from bigdata.skewTable) t1
  JOIN bigdata.normalTable t2
  ON t1.id = t2.key

提供表名,列名,倾斜值

-- 单值倾斜
SELECT /*+ SKEW('t1', 'id', 0) */ *
  FROM leftTable t1
  JOIN rightTable t2
  ON t1.id = t2.key
 
-- 多值倾斜
SELECT /*+ SKEW('t1', 'id', (0, 1, 2)) */ *
  FROM leftTable t1
  JOIN rightTable t2
  ON t1.id = t2.key

编译&配置

编译打包

git clone 待添加
mvn clean package
  • cluster 模式使用 spark.yarn.dist.jars 添加 hdfs 上的 jar 包
  • client 模式使用 spark.driver.extraClassPath 添加本地 jar 包

spark-default.conf 中配置参数

spark.yarn.dist.jars hdfs://ns/path/to/spark-sql-extension-1.0.1.jar 
spark.sql.extensions cn.tongdun.sql.TDExtensions

或者启动时增加

--conf spark.driver.extraClassPath=/path/to/spark-sql-extension-1.0.1.jar 
--conf spark.sql.extensions=cn.tongdun.sql.TDExtensions

Test

sql执行计划发生变化

-- before skew hint 
'Project [*]
+- 'Join Inner, ('t1.id = 't2.id)
   :- 'SubqueryAlias `t1`
   :  +- 'UnresolvedRelation `leftTB`
   +- 'SubqueryAlias `t2`
      +- 'UnresolvedRelation `rightTB`
  
-- after skew hint
'Project [*]
+- 'Repartition 200, false
   +- 'Union
      :- 'Join Inner, ('t1.id = 't2.id)
      :  :- 'Filter NOT 't1.id IN (5,6)
      :  :  +- 'SubqueryAlias `t1`
      :  :     +- 'UnresolvedRelation `leftTB`
      :  +- 'Filter NOT 't2.id IN (5,6)
      :     +- 'SubqueryAlias `t2`
      :        +- 'UnresolvedRelation `rightTB`
      +- 'Join Inner, ('t1.id = 't2.id)
         :- 'Filter 't1.id IN (5,6)
         :  +- 'SubqueryAlias `t1`
         :     +- 'UnresolvedRelation `leftTB`
         +- 'ResolvedHint (broadcast)
            +- 'Filter 't2.id IN (5,6)
               +- 'SubqueryAlias `t2`
                  +- 'UnresolvedRelation `rightTB`

运行时间变化
使用 skew hint 的前后对比图,根据数据倾斜程度的不同,提升会有浮动,在数倍到数十倍之间
在这里插入图片描述
项目地址: 待添加

局限性

  1. 使用 skew hint 有额外的性能开销,在倾斜的不严重的场景下,并不一定有优势,因为需要遍历两次数据。
  2. 如果两张表都有严重倾斜的数据,会导致数据膨胀,无法通过该方式解决

参考

databricks skew join

猜你喜欢

转载自blog.csdn.net/lsshlsw/article/details/88412896
今日推荐