一、数据倾斜概述
- 数据倾斜原理
- 在进行任务计算shuffle操作的时候,少数的task被分配到了大量的数据,是其他task的十倍甚至百倍,导致最终整个spark任务计算特别慢。
- 如何定位原因
- 主要是根据log日志信息去定位
- 分析定位逻辑,由于代码中有大量的shuffle操作,一个job会划分为多个stage,我们可以观察任务的UI界面,观察是哪一个stage中的task分配主句不均匀,根据代码逻辑分析是哪个算子导致的
- 某个task莫名内存溢出的情况,我们可以直接看yarn-client模式下本地log的异常栈,或者是通过YARN查看yarn-cluster模式下的log中的异常栈,但task内存溢出并不一定是数据倾斜引起的。
- 查看导致数据倾斜的key的分布情况,如果是Spark SQL导致的数据倾斜,那就看看所查询表中key的分布情况,如果是RDD执行shuffle算子导致的数据倾斜,则可以先simple抽取数据,countbykey查看各个key的数据量分布,collect/take到客户端打印查看。
- 数据倾斜的后果
- spark中的stage的执行时间受限于最后完成的task,拖慢了整个程序的运行速度
- 过多数据在同一个task中运行,会把executor内存撑爆,导致OOM
二、使用Hive ETL预处理数据
- 适用场景:Hive表中数据本身key分布不均匀
- 实现思路:对Hive进行ETL预处理,将spark中的shuffle操作提到ETL中,这样spark中就不会有shuffle操作了
- 优点:实现起来比较简单,spark性能大幅提升
- 缺点:治标不治本,Hive ETL中还是会发生数据倾斜
三、过滤少数导致倾斜的key
- 适用场景:只有少数key导致数据倾斜
- 实现思路:如果判断导致倾斜的key对于作业的执行和计算结果不是特别重要,就直接过滤掉。在Spark SQL中可以使用where子句过滤掉,在RDD中使用filter算子过滤掉
- 优点:实现简单,效果很好
- 缺点:适用场景不多
四、提高shuffle操作的并行度
- 适用场景:数据倾斜无法避免,需要被处理
- 实现思路:设置参数:spark.sql.shuffle.partitions,或者是给shuffle算子传入一个参数,比如reduceByKey(1000),可以让原本分配给一个task的多个key分配给多个task
- 优点:可以有效缓解数据倾斜的影响
- 缺点:没有根除数据倾斜,效果有限
五、两阶段聚合(局部聚合+全局聚合)
- 适用场景:对RDD执行聚合类算子,使用Spark SQL中的group by语句进行聚合
- 实现思路:先给每个key都加上一个随机数前缀,进行局部聚合,再将前缀去掉进行全局聚合。这样将原本相同的key分散到多个task上做局部聚合,再进行全局聚合
- 优点:对于聚合类的shuffle操作导致的数据倾斜效果不错,通常可以解决掉数据倾斜
- 缺点:仅仅适用于聚合类的shuffle操作
六、将reduce join转为map join
- 适用场景:对RDD进行join操作,或者Spark SQL中使用join语句,而且join操作中的一个RDD或者表的数据量比较小
- 实现思路:不使用join算子,而使用Broadcast变量与map类算子实现join操作,完全规避掉shuffle操作。将较小RDD中的数据通过collect算子拉取到Driver端的内存,对其创建一个Broadcast变量,对另一个RDD进行map,判断如果key相同则将两者的数据按需要的方式拼接起来。
- 优点:规避了shuffle操作
- 缺点:适用场景少,适用于一个大表和一个小表的情况
七、采样倾斜key并分拆join操作
- 适用场景:两个RDD/Hive表进行join的时候数据量都比较大,适用于两个表中其中一个表key倾斜,而另一个表key数据分布均匀的情况
- 实现思路:1.先使用采样,采出一份样本,统计一下每个key的数量,取出最大的几个key 2.将这几个key对应的数据从原来的RDD中拆分出来,形成一个单独的RDD,并给每个key都打上0-n随机数作为前缀,不会导致倾斜的key数据形成另外一份RDD 3.将需要join的另一个RDD也过滤好生成两份RDD,并将有倾斜key的RDD每条数据都加上0-n的前缀,膨胀成n条 4. 加了随机前缀的RDD与膨胀的RDD进行join,这样就将相同的key打散成n份,分散到多个task中去了 5.将另外两个RDD也进行join,并将两次结果union合并起来
- 优点:对于少数key导致的数据倾斜使用该方式最有效的打散key
- 缺点:如果导致倾斜的key比较多的话,这种方式不太合适
八、使用随机前缀和扩容RDD进行join
- 适用场景:大量key导致的数据倾斜,这时chaifenkey就没有意义了
- 实现思路:对于其中一个RDD每条都加上n以内的随机数,另一个RDD则膨胀n倍,两个RDD进行join
- 优点:对于join类的数据倾斜基本都可以解决
- 缺点:比较吃内存