内容简介:我在执行以下sql语句的时候遇到了执行内存不够的问题,原先我还以为是内存不够,然后加大了内存,但是还是执行失败。后面我在spark ui中看到了该sql的执行计划,该sql语句执行了Map-side Join操作,但是spark把a表当成了小表,准备把a表broadcast到其他的节点,然后就是一直卡在这步broadcast操作上。 造成上述问题的原因就是spark认为a表是一个小表,但是在spark ui上明显可以看到a表读了很多的行。但是为什么spark还会认为a表是一个小表呢?原因是spark判断一个
我在执行以下 sql 语句的时候遇到了执行内存不够的问题,原先我还以为是内存不够,然后加大了内存,但是还是执行失败。
// a是一个几亿行的大表,b是一个只有几十行的小表。a和b都是由hive创建的表 select * from a where id not in (select id from b)
后面我在spark ui中看到了该sql的执行计划,该sql语句执行了Map-side Join操作,但是spark把a表当成了小表,准备把a表broadcast到其他的节点,然后就是一直卡在这步broadcast操作上。 造成上述问题的原因就是spark认为a表是一个小表,但是在spark ui上明显可以看到a表读了很多的行。但是为什么spark还会认为a表是一个小表呢?原因是spark判断一个hive表的大小会用hive的metastore数据来判断,因为我们的a表没有执行过ANALYZE TABLE,自然a表的metastore里面的数据就不准确了。
解决方法?
既然知道了问题,要解决就很简单了。有如下几个解决方法:
设置spark.sql.statistics.fallBackToHdfs=True
该参数能让spark直接读取hdfs的文件大小来判断一个表达大小,从而代替从metastore里面的获取的关于表的信息。这样spark自然能正确的判断出表的大小,从而使用b表来进行broadcast。
使用hint
在使用sql语句执行的时候在sql语句里面加上mapjoin的注释,也能够达到相应的效果,比如把上述的sql语句改成:
select /*+ BROADCAST (b) */ * from a where id not in (select id from b)
这样spark也会使用b表来进行broadcast。
使用spark代码的方式
使用broadcast函数就能达到此效果:
from pyspark.sql.functions import broadcast broadcast(spark.table("b")).join(spark.table("a"), "id").show()
拓展知识
什么时候spark才会使用Map-side Join?
只有当要进行join的表的大小小于spark.sql.autoBroadcastJoinThreshold(默认是10M)的时候,才会进行mapjoin。
总结
上述的优化同样也适用于其他的计算引擎,比如Impala通过hint和执行表的位置调整也能够优化join操作,通过explain也可以查看sql的执行计划,然后再进行优化。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 命令执行与代码执行的小结
- Go如何保证gorountine执行完毕后继续执行
- [译] 理解 JavaScript 中的执行上下文和执行栈
- 熔断器 Hystrix 源码解析 —— 命令执行(二)之执行隔离策略
- 面试:谈谈你对 MyBatis 执行过程之 SQL 执行过程理解
- 前端进击的巨人(一):执行上下文与执行栈,变量对象
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。