SparkSql不同写法的一些坑(性能优化)

 说三种情况,看大家有没有遇到类似的场景。

第一种情况:

这种情况也是我经常会遇到的一个场景,之前也有同学拿着sql来问,说这样写会不会影响运行效率:

select  
  tmp.A 
from (select A,B from testdata2) tmp

结论是

不用担心,这样写完全可以被优化    

== Analyzed Logical Plan ==
Project [A#3]
+- SubqueryAlias tmp
   +- Project [A#3, B#4]
      +- SubqueryAlias testdata2
         +- View (`testData2`, [a#3,b#4])
            +- SerializeFromObject [knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).a AS a#3, knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).b AS b#4]
               +- ExternalRDD [obj#2]  
                         
== Optimized Logical Plan ==
Project [A#3]
+- SerializeFromObject [knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).a AS a#3]
   +- ExternalRDD [obj#2]

从执行计划上清晰的看到,最终被优化成

select A from testdata2

这样的效果,主要是 ColumnPruning(列裁剪) 和 CollapseProject(合并Project)这两种优化器起到作用。

第二种情况:

这种情况之前一直没在意,发现我写过的一些代码里默默都这么用了

-- 其中myudf是一个自定义UDF函数,返回一个数组
 select 
   myudf(A,B)[0] as a1,
   myudf(A,B)[1] as a2,
   myudf(A,B)[2] as a3 
 from testdata2
 

这里myudf(A,B)执行几遍?

结论是

会执行三遍。

如果myudf是一个很复杂的函数,要合并两个非常复杂的字符串A和B,这个也是我工作中的一个场景。这样的话,执行三遍,非常不合理。

怎么办?

改写:

-- 其中myudf是一个自定义UDF函数,返回一个数组
 select 
   atmp[0] as a1,
   atmp[1] as a2,
   atmp[2] as a3 
 from 
 (select 
   myudf(A,B) as atmp
 from testdata2
 ) tmp

这样改写就万事大吉了嘛?

在sparksql branch3.3 这样改写完全没问题,但毕竟3.3是新版本,大部分人都还没用上,换到3.3之前的版本,分分钟再给变(优化)成第一种写法(执行三遍的)。

branch3.3(是ok的,内层先计算出myudf的值,外层用计算过的值取数):

== Analyzed Logical Plan ==
Project [atmp#10[0] AS a1#11, atmp#10[1] AS a2#12, atmp#10[2] AS a3#13]
+- SubqueryAlias tmp
   +- Project [myudf(A#3,B#4) AS atmp#10]
      +- SubqueryAlias testdata2
         +- View (`testData2`, [a#3,b#4])
            +- SerializeFromObject [knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).a AS a#3, knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).b AS b#4]
               +- ExternalRDD [obj#2]
​
​
​
== Optimized Logical Plan ==
Project [atmp#10[0] AS a1#11, atmp#10[1] AS a2#12, atmp#10[2] AS a3#13]
+- Project [myudf(A#3,B#4) AS atmp#10]
   +- SerializeFromObject [knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).a AS a#3, knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).b AS b#4]
      +- ExternalRDD [obj#2]

branch3.3之前的版本(不管我们愿不愿意,都给合并喽):

== Analyzed Logical Plan ==
Project [atmp#10[0] AS a1#11, atmp#10[1] AS a2#12, atmp#10[2] AS a3#13]
+- SubqueryAlias tmp
   +- Project [concat(array(A#3), array(B#4)) AS atmp#10]
      +- SubqueryAlias testdata2
         +- SerializeFromObject [knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).a AS a#3, knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).b AS b#4]
            +- ExternalRDD [obj#2]
            
== Optimized Logical Plan ==            
Project [myudf(A#3,B#4)[0] AS a1#11, myudf(A#3,B#4)[1] AS a2#12, myudf(A#3,B#4)[2] AS a3#13]
+- SerializeFromObject [knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).a AS a#3, knownnotnull(assertnotnull(input[0, org.apache.spark.sql.test.SQLTestData$TestData2, true])).b AS b#4]
   +- ExternalRDD [obj#2]

一直不信,怎么会不这么不智能,具体原因是啥?该怎么避免?这个在上周六的直播分享里讲过了。

第三种情况:

这种也会经常遇到,并且也会经常被其他朋友问到能不能被优化

// 其中用collect_set来代表聚合函数
select 
  collect_set(a)[0] as c1,  
  collect_set(a)[1] as c2, 
  collect_set(a)[3] as c3
from testdata2 
group by b

这里的collect_set(a)会执行几遍?

结论是

执行一遍。

这样类似的还有:count(xxx),count(distinct xxx) 等等,聚合函数在重复用时,不用担心,sparksql会给优化。所以,我们在写代码时就不用考虑再在外面写一层,从而避免多写一层,造成数据多流转一次的浪费。

看看吧,不同的情况,会有不同的优化结果,如果知道原理,就能避开一些坑。

以上!

相关阅读:SparkSql数组操作的N种骚气用法


Hey!

我是小萝卜算子

欢迎关注:数据仓库践行者

分享是最好的学习,这里记录我对数据仓库的实践的思考和总结

每天学习一点点

知识增加一点点

思考深入一点点

在成为最厉害最厉害最厉害的道路上

很高兴认识你

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小萝卜算子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值