Spark数据倾斜解决方案

数据倾斜的解决,跟之前讲解的性能调优,有一点异曲同工之妙。性能调优,跟大家讲过一个道理,“重剑无锋”。性能调优,调了半天,最有效,最直接,最简单的方式,就是加资源,加并行度,注意RDD架构(复用同一个RDD,加上cache缓存);shuffle、jvm等,都是次要的。数据倾斜的解决方案,第一个方案和第二个方案,是最朴素、最简谱、最直接、最有效、最简单的,解决数据倾斜问题的方案。重剑无锋。后面的五个方案,尤其是最后4个方案,都是那种特别炫酷的方案,像双重group聚合方案、sample抽样分解聚合方案;但是如果碰到了数据倾斜的问题,上来就先考虑考虑第一个和第二个方案,能不能做,如果能做的话,后面的5个方案,都不用去搞了,有效、简单、直接,效果是非常之好的,彻底根除了数据倾斜的问题。

1、聚合源数据

我们做一些聚合的操作(groupByKey、reduceByKey),groupByKey,就是拿到每个key对应的values;reduceByKey,就是对每个key对应的values执行一定的计算。只是现在这些操作,比如groupByKey和reduceByKey,包括之前说的join,都是在spark作业中执行的。

spark作业的数据来源,90%的情况下,数据来源都是hive表(hdfs上存储的大数据)。

有了spark以后,hive适合做离线的,晚上凌晨跑的,ETL(extract transform load,数据的采集、清洗、导入),hive sql,去做这些事情,从而去形成一个完整的hive中的数据仓库;说白了,数据仓库,就是一堆表。spark作业的源表,hive表,其实通常情况下来说,也是通过某些hive etl生成的。hive etl可能是晚上凌晨在那儿跑。今天跑昨天的数据。

数据倾斜,某个key对应的80万数据,某些key对应几百条,某些key对应几十条;

  • 1、我们直接在生成hive表的hive etl中,对数据进行聚合。比如按key来分组,将key对应的所有的values,全部用一种特殊的格式,拼接到一个字符串里面去,比如“key=sessionid, value: action_seq=1|user_id=1|search_keyword=火锅|category_id=001;action_seq=2|user_id=1|search_keyword=涮肉|category_id=001”。

    对key进行group,在spark中,拿到key=sessionid,values;我们在hive etl中,直接对key进行了聚合,那么也就意味着,每个key就只对应一条数据。在spark中,就不需要再去执行groupByKey+map这种操作了,直接对每个key对应的values字符串,map操作,进行你需要的操作即可。

    这时在spark中,可能对这个操作,就不需要执行shffule操作了,也就根本不可能导致数据倾斜。

  • 2、我们直接对每个key在hive etl中进行聚合,对所有values聚合一下,不一定是拼接起来,可能是直接进行计算。reduceByKey,计算函数,应用在hive etl中,每个key的values。

  • 3、我们有时候可能没有办法对每个key,就聚合出来一条数据;那么也可以做一个妥协;对每个key对应的数据,10万条;有好几个粒度,比如10万条里面包含了几个城市、几天、几个地区的数据,现在放粗粒度;直接就按照城市粒度,做一下聚合,几个城市,几天、几个地区粒度的数据,都给聚合起来。比如说city_id date area_idselect ... from ... group by city_id

    尽量去聚合,减少每个key对应的数量,也许聚合到比较粗的粒度之后,原先有10万数据量的key,现在只有1万数据量。减轻数据倾斜的现象和问题。

2、过滤导致倾斜的key

如果我们可以拒接某些数据,在spark作业中直接就摒弃掉,不使用。比如说,总共有100万个key,只有2个key,是数据量达到10万的,其他所有的key,对应的数量都是几十。这个时候,可以去取舍,如果业务和需求可以理解和接受的话,在我们从hive表查询源数据的时候,直接在sql中用where条件,过滤掉某几个key。

那么这几个原先有大量数据,会导致数据倾斜的key,被过滤掉之后,那么在你的spark作业中,自然就不会发生数据倾斜了。

3、提高shuffle操作reduce并行度

将reduce task的数量,变多,就可以让每个reduce task分配到更少的数据量,这样的话,也许就可以缓解,或者甚至是基本解决掉数据倾斜的问题。

  • 怎么操作给我们所有的shuffle算子,比如groupByKey、countByKey、reduceByKey。在调用的时候,传入进去一个参数,一个数字,代表了那个shuffle操作的reduce端的并行度。那么在进行shuffle操作的时候,就会对应着创建指定数量的reduce task。

    这样的话,就可以让每个reduce task分配到更少的数据。基本可以缓解数据倾斜的问题。

    比如说,原本某个task分配数据特别多,直接OOM,内存溢出了,程序没法运行,直接挂掉。按照log,找到发生数据倾斜的shuffle操作,给它传入一个并行度数字,这样的话,原先那个task分配到的数据,肯定会变少,可以避免OOM的情况,程序至少是可以跑得。

  • 缺点这个是治标不治本,因为没有从根本上改变数据倾斜的本质和问题,不像第一个和第二个方案,直接避免了数据倾斜的发生,原理没有改变,只是尽可能地去缓解和减轻shuffle reduce task的数据压力,以及数据倾斜的问题。

  • 实际生产环境中的经验

    那么,如果出现第二种情况的话,就立即放弃第三种方案,开始去尝试和选择后面的四种方案

    • 1、如果最理想的情况下,提升并行度以后,减轻了数据倾斜的问题,或者甚至可以让数据倾斜的现象忽略不计,那么就最好,就不用做其他的数据倾斜解决方案了。

    • 2、不太理想的情况下,就是比如之前某个task运行特别慢,要5个小时,现在稍微快了一点,变成了4个小时;或者是原先运行到某个task,直接OOM,现在至少不会OOM了,但是那个task运行特别慢,要5个小时才能跑完。

4、使用随机key实现双重聚合

使用随机key实现双重聚合

1、原理

Spark数据倾斜解决方案_第1张图片

Spark数据倾斜解决方案_第2张图片

图一为直接聚合,图二为双重聚合;整个过程分为三步

  • 第一步,第一轮聚合的时候,对key进行打散,将原先一样的key,变成不一样的key,相当于是将每个key分为多组;

  • 第二步,接着针对多个key组,进行key的局部聚合;

  • 第三步,最后,再去除掉每个key的前缀,然后对所有的key,进行全局的聚合。

2、使用场景这种方案对groupByKey、reduceByKey造成的数据倾斜,有比较好的效果。

如果说,之前的第一、第二、第三种方案,都没法解决数据倾斜的问题,那么就只能依靠这一种方式了。

5、将reduce join转换为map join

Spark数据倾斜解决方案_第3张图片

普通join:肯定是要走shuffle(reduce join)先将所有相同的key,对应的values,汇聚到一个task中,然后再进行join

Spark数据倾斜解决方案_第4张图片

map join:先将一个RDD广播broadcast出去,然后就可以在一个task中执行map join操作,这样就可以避免shuffle操作

使用场景:如果两个RDD要进行join,其中一个RDD是比较小的。一个RDD是100万数据,一个RDD是1万数据(一个RDD是1亿数据,一个RDD是100万数据);其中一个RDD必须是比较小的,broadcast出去那个小RDD的数据以后,就会在每个executor的block manager中都驻留一份,注意,要确保你的内存足够存放那个小RDD中的数据;这种方式下,根本不会发生shuffle操作,肯定也不会发生数据倾斜,从根本上杜绝了join操作可能导致的数据倾斜的问题;

对于join中有数据倾斜的情况,如果某个RDD比较小的情况下,要尽量第一时间先考虑这种方式,效果非常好。

对于join这种操作,不光是考虑数据倾斜的问题,即使是没有数据倾斜问题,也完全可以优先考虑,用我们讲的这种高级的reduce join转map join的技术,不要用普通的join,去通过shuffle,进行数据的join,可以通过简单的map,使用map join的方式,牺牲一点内存资源;在可行的情况下,优先这么使用,对于性能也会有不少的提升。

6、sample 采样倾斜key单独进行join

实现思路:将发生数据倾斜的key,单独拉出来,放到一个RDD中去;就用这个原本会倾斜的key RDD跟其他RDD,单独去join一下,这个时候,key对应的数据,可能就会分散到多个task中去进行join操作;如果这个key跟之前其他的key混合在一个RDD中时,肯定是会导致一个key对应的所有数据,都到一个task中去,就会导致数据倾斜。

Spark数据倾斜解决方案_第5张图片

Spark数据倾斜解决方案_第6张图片

使用场景:对于join,优先采用第5种方案,reduce join转换map join。但是如果两个RDD数据都比较大,那么就不能那么搞了。

针对你的RDD的数据,你可以自己把它转换成一个中间表,或者是直接用countByKey()的方式,计算这个RDD各个key对应的数据量;此时如果你发现整个RDD就一个,或者少数几个key,是对应的数据量特别多,此时可以采用咱们的这种方案,单拉出来哪个最多的key;单独进行join,尽可能地将key分散到各个task上去进行join操作。

优化:当咱们单拉出来了一个或者少数几个可能会产生数据倾斜的key,然后还可以进行更加优化的一个操作;对于那个key,从另外一个要join的表中,也过滤出来一份数据,比如可能就只有一条数据。userid2infoRDD,一个userid key,就对应一条数据。

然后呢,采取对那个只有一条数据的RDD,进行flatMap操作,打上100个随机数,作为前缀,返回100条数据。

单独拉出来的可能产生数据倾斜的RDD,给每一条数据,都打上一个100以内的随机数,作为前缀。

再去进行join,肯定可以将数据进行打散(打散成100份,甚至1000份,2000份,去进行join,那么就肯定没有数据倾斜的问题了),去进行join;join完以后,可以执行map操作,去将之前打上的随机数,给去掉,然后再和另外一个普通RDD join以后的结果,进行union操作。

这种情况下,还可以配合上,提升shuffle reduce并行度,join(rdd, 1000)。通常情况下,效果还是非常不错的。

缺点:如果一个RDD中,导致数据倾斜的key,特别多;那么此时,最好还是不要这样了;还是使用我们最后一个方案,终极的join数据倾斜的解决方案。

7、使用随机数以及扩容表进行join

当采用随机数和扩容表进行join解决数据倾斜的时候,就代表着,你的之前的数据倾斜的解决方案,都没法使用。这个方案是没办法彻底解决数据倾斜的,更多的,是一种对数据倾斜的缓解。

操作步骤:

  • 1、选择一个RDD,要用flatMap,进行扩容,将每条数据,映射为多条数据,每个映射出来的数据,都带了一个n以内的随机数,通常来说,会选择10。

  • 2、将另外一个RDD,做普通的map映射操作,每条数据,都打上一个10以内的随机数。

  • 3、最后,将两个处理后的RDD,进行join操作。

Spark数据倾斜解决方案_第7张图片局限性:

  • 1、因为你的两个RDD都很大,所以你没有办法去将某一个RDD扩得特别大,一般咱们就是10倍。

  • 2、如果就是10倍的话,那么数据倾斜问题,的确是只能说是缓解和减轻,不能说彻底解决。

你可能感兴趣的:(数据分析,HIVE,Spark,spark,big,data,hive)