文章目录
- 背景
- skew hint
- 实现方式
- 原理
- 用法
- 语法
- 编译&配置
- Test
- 局限性
- 参考
背景
使用 Join 时,如果出现数据倾斜就会导致OOM或者单task长时间执行的现象,如果是大小表关联的场景,还可以使用 MAPJOIN 的方式来解决,如果遇到两张大表可能就束手无策了。
尝试通过 spark sql extensions
自定义 skew hint
语法解决上述问题。
skew hint
实现方式
使用spark sql extensions
的扩展功能,增加自定义 hint 在 sql parser 层对相关逻辑进行处理,这种方式无需对spark源码进行修改,对比之前 aspect的实现方式 也更加优雅
原理
- 根据倾斜表和倾斜字段自动分析倾斜值,如果在sql中明确提供倾斜值则会跳过该过程。
- 两表针对倾斜值(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 的前后对比图,根据数据倾斜程度的不同,提升会有浮动,在数倍到数十倍之间
局限性
- 使用 skew hint 有额外的性能开销,在倾斜的不严重的场景下,并不一定有优势,因为需要遍历两次数据。
- 如果两张表都有严重倾斜的数据,会导致数据膨胀,无法通过该方式解决
参考