|NO.Z.00109|——————————|BigDataEnd|——|Hadoop&Spark.V07|——|Spark.v07|Spark 原理 源码|数据倾斜&数据倾斜处理|

一、数据倾斜处理
### --- 做好数据预处理:

~~~     过滤key中的空值
~~~     消除数据源带来的数据倾斜(文件采用可切分的压缩方式)
~~~     数据倾斜产生的主要原因:Shuffle + key分布不均
### --- 处理数据倾斜的基本思路:

~~~     消除shuffle
~~~     减少shuffle过程中传输的数据
~~~     选择新的可用于聚合或join的Key(结合业务)
~~~     重新定义分区数
~~~     加盐强行打散Key
~~~     key.hashCode % reduce个数 = 分区号

一、避免shuffle
### --- 避免shuffle

~~~     Map端的join是典型的解决方案
~~~     可以完全消除Shuffle,进而解决数据倾斜
~~~     有很强的适用场景(大表和小表关联),典型的大表与小表的join,其他场景不合适
二、减少 Shuffle 过程中传输的数据
### --- 减少 Shuffle 过程中传输的数据

~~~     使用高性能算子,避免使用groupByKey,用reduceByKey或aggregateByKey替代
~~~     没有从根本上解决数据分配不均的问题,收效有限,使用场景有限
三、选择新的可用于聚合或join的Key
### --- 选择新的可用于聚合或joinKey

~~~     从业务出发,使用新的key去做聚合或join。如当前key是【省 城市 日期】,
~~~     在业务允许的情况下选择新的key【省 城市 区 日期】,有可能 解决或改善 数据倾斜问题
~~~     存在的问题:这样的key不好找;或者找到了新的key也不能解决问题
四、改变Reduce的并行度
### --- 改变Reduce的并行度

~~~     key.hashCode % reduce个数 = 分区号
~~~     变更 reduce 的并行度。理论上分区数从 N 变为 N-1 有可能解决或改善数据倾斜
~~~     一般情况下这个方法不管用,数据倾斜可能是由很多key造成的,
~~~     但是建议试试因为这个方法非常简单,成本极低
~~~     可能只是解决了这一次的数据倾斜问题,非长远之计
~~~     缺点:适用性不广;优点:简单
### --- 源码提取说明

  // 2000W条数据对Key做了处理,使其在后面的shuffle中产生数据倾斜
  val rdd = sc.makeRDD(1 to 30000000)
  val rdd1 = rdd.map(x => (if (x>3000000) (x%3000000)*6 else x, 1))
  // 执行过程中可以感觉到有一个作业执行的慢,从Web界面中可以很清楚的看见数据倾斜
  // 备注:shuffle write是均衡的,shuffle read不均衡
  rdd1.groupByKey().mapPartitionsWithIndex{(index, iter)=>
    val elementCount = iter.toArray.length
    Iterator(index + ":" + elementCount)
  }.collect
  // 调整(缩小)了并行度,增加了shuffle,但执行的时间更快了。数据倾斜猛于虎,在这里数据倾斜对系统的影响超过了shuffle
  rdd1.repartition(5).groupByKey().mapPartitionsWithIndex{(index, iter)=>
    val elementCount = iter.toArray.length
    Iterator(index + ":" + elementCount)
  }.collect
  // repartition在重分区的过程中会产生shuffle,而coalesce不会,这样可以减少一次shuffle,进而提高效率
  rdd1.coalesce(5).groupByKey().mapPartitionsWithIndex{(index, iter)=>
    val elementCount = iter.toArray.length
    Iterator(index + ":" + elementCount)
  }.collect
  // 定义分区器,减少了一次shuffle,执行速度更快
  rdd1.groupByKey(new org.apache.spark.HashPartitioner(5)).mapPartitionsWithIndex{(index,
                                                                                   iter)=>
    val elementCount = iter.toArray.length
    Iterator(index + ":" + elementCount)
  }.collect
五、加盐强行打散Key
### --- 加盐强行打散Key

~~~     shuffle + key不能分散
### --- 两阶段聚合

~~~     加盐打散key。给每个key都加一个随机数,如10以内的随机数。此时key就被打散了
~~~     局部聚合。对打上随机数的数据,执行一次聚合操作,得到结果
~~~     全局聚合。将各个key的前缀去掉,再进行一次聚合操作,得到最终结果
### --- 两阶段聚合的优缺点:

~~~     对于聚合类的shuffle操作导致的数据倾斜,效果不错。通常都可以解决掉数据倾斜,
~~~     至少是大幅度缓解数据倾斜,将Spark作业的性能提升数倍以上
~~~     仅适用于聚合类的shuffle操作,适用范围相对较窄。
~~~     如果是join类的shuffle操作,还得用其他的解决方案
六、采样倾斜key并拆分join操作
### --- 采样倾斜key并拆分join操作

~~~     业务场景:两个RDD/两张表进行 join 的时候,数据量都比较大。
~~~     使用场景:计算两个RDD/两张表中的key分布情况。如果出现数据倾斜,
~~~     是其中一个RDD/Hive表中的少数几个key的
~~~     数据量过大,而另一个RDD/Hive表中的所有key都分布比较均匀,那么采用这个解决方案比较合适。
### --- 处理步骤:

~~~     对包含少数几个数据量过大的key的那个RDD,通过sample算子采样出一份样本来,
~~~     然后统计一下每个key的数量,计算出数据量最大的是哪几个key;
~~~     将这几个key对应的数据从原来的RDD中拆分出来,形成一个单独的RDD,
~~~     并给每个key都打上n以内的随机数作为前缀,而不会导致倾斜的大部分key形成另外一个RDD;
~~~     将需要join的另一个RDD,也过滤出来那几个倾斜key对应的数据并形成一个单独的RDD,
~~~     将每条数据膨胀成n条数据,这n条数据都按顺序附加一个0~n的前缀,
~~~     不会导致倾斜的大部分key也形成另外一个RDD;
~~~     再将附加了随机前缀的独立RDD与另一个膨胀n倍的独立RDD进行join,
~~~     此时就可以将原先相同的key打散成n份,分散到多个task中去进行join了;
~~~     另外两个普通的RDD就照常join即可;
~~~     最后将两次join的结果使用union算子合并起来即可,就是最终的join结果。
### --- 使用随机前缀和扩容再进行join

~~~     业务场景:如果在进行join操作时,RDD中有大量的key导致数据倾斜,
~~~     进行分拆key没什么意义,此时就只能使用最后一种方案来解决问题了。
### --- 处理步骤:

~~~     选一个RDD,将每条数据都打上一个n以内的随机前缀(打散)
~~~     对另外一个RDD进行扩容,将每条数据都扩容成n条数据,
~~~     扩容出来的每条数据都依次打上一个0~n的前缀
~~~     将两个处理后的RDD进行join即可
~~~     # 优缺点:
~~~     如果两个RDD都很大,那么将RDD进行N倍的扩容显然行不通
~~~     使用扩容的方式通常能缓解数据倾斜,不能彻底解决数据倾斜问题
### --- 小结:

~~~     数据倾斜问题的解决没有银弹,通常是找到数据倾斜发生的原因,然后见招拆招;
~~~     在实践中很多情况下,如果只是处理较为简单的数据倾斜场景,
~~~     使用上述方案中的某一种基本就可以解决。
~~~     但是如果要处理一个较为复杂的数据倾斜场景,可能需要将多种方案组合起来使用。
~~~     需要对这些方案的思路和原理都透彻理解之后,在实践中根据各种不同的情况,
~~~     灵活运用多种方案,来解决自己遇到
~~~     的数据倾斜问题。

 
 
 
 
 
 
 
 
 

Walter Savage Landor:strove with none,for none was worth my strife.Nature I loved and, next to Nature, Art:I warm'd both hands before the fire of life.It sinks, and I am ready to depart
                                                                                                                                                   ——W.S.Landor

 

 

posted on   yanqi_vip  阅读(33)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示