一、数据倾斜介绍与定位

1、数据倾斜的原理

 在执行shuffle操作时是按照key来进行values的数据输出、拉取和聚合,同一个key的values,一定是分配到一个reduce task进行处理的。假如多个key对应的values总共是90万,可能某个key对应了88万数据,分配到了一个task上执行;另外两个task可能各分配到了1万数据,也可能是数百个key,对应的1万条数据。第一个和第二个task,各分配到了1万数据,那么可以1万条数据需要10分钟计算完毕;第三个task要处理88万条,88 * 10 = 880分钟。

2、数据倾斜的两种表现

(1)大部分task执行的特别快,少部分task跑得特别慢(可用client模式,如standalone client,yarn client,本地机器主要一执行spark-submit脚本,就开始打印log)

(2)部分task出现内部溢出OOM,如JVM Out Of Memory,task failed、task lost,resubmitting task。

3、数据倾斜定位

数据数据倾斜的原因,基本只可能是因为发生了shuffle操作,在shuffle的过程中出现了数据倾斜的问题。

(1)在程序中寻找会产生shuffle的算子:如groupByKeycountByKeyreduceByKeyjoin

(2)查看log日志:log一般会报是在你的哪一行代码,导致了OOM异常;或者呢,看log,看看是执行到了第几个stage。

二、解决方案

1、聚合数据源

1.1 聚合数据源方案一:

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

spark作业的数据来源,通常是哪里呢?90%的情况下,数据来源都是hive表(hdfs,大数据分布式存储系统)。hdfs上存储的大数据。hive表,hive表中的数据,通常是怎么出来的呢?有了spark以后,hive比较适合做什么事情?hive就是适合做离线的,晚上凌晨跑的,ETL(extract transform load,数据的采集、清洗、导入),hive sql,去做这些事情,从而去形成一个完整的hive中的数据仓库;说白了,数据仓库,就是一堆表。spark作业的源表,hive表,其实通常情况下来说,也是通过某些hive etl生成的。hive etl可能是晚上凌晨在那儿跑。今天跑昨天的数据。

数据倾斜,某个key对应的80万数据,某些key对应几百条,某些key对应几十条;现在,咱们直接在生成hive表的hive etl中,对数据进行聚合。比如按key来分组,将key对应的所有的values,全部用一种特殊的格式,拼接到一个字符串里面去,比如“key=sessionid, value: action_seq=1|user_id=1|search_keyword….”。

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

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

1.2 聚合数据源方案二:

如果没有办法对每个key都聚合一条数据,那么也可以做一个妥协;对每个key对应的数据,10万条;有好几个粒度,比如10万条里面包含了几个城市、几天、几个地区的数据,现在放粗粒度;直接就按照城市粒度,做一下聚合,几个城市,几天、几个地区粒度的数据,都给聚合起来。比如说

select ... from ... group by city_id,date,area_id

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

2、提高shuffle操作reduce并行度

如果第一种方法不适合做。那么采用第二种方法:提高shuffle操作的reduce并行度

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

2.1 具体操作

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

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

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

2.2 提升shuffle reduce并行度的缺陷

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

2.3 实际生产环境中的经验

  • a、如果最理想的情况下,提升并行度以后,减轻了数据倾斜的问题,或者甚至可以让数据倾斜的现象忽略不计,那么就最好。就不用做其他的数据倾斜解决方案了。
  • b、不太理想的情况下,就是比如之前某个task运行特别慢,要5个小时,现在稍微快了一点,变成了4个小时;或者是原先运行到某个task,直接OOM,现在至少不会OOM了,但是那个task运行特别慢,要5个小时才能跑完。

那么,如果出现第二种情况的话,就立即放弃这种方法,开始去尝试和选择后面的方法解决。

3、随机key实现双重聚合

原理介绍:

spark sql cross join 数据倾斜 spark 解决数据倾斜_数据倾斜

具体操作:

  • 1)第一轮聚合的时候,对key进行打散,将原先一样的key变成不一样的key,相当一起是将每个key分为多组;
  • 2)先针对多个组,进行key的局部聚合;接着再除掉每个key的前缀,然后再对所有的key进行全局的聚合

该方法对groupByKey、reduceByKey造成的数据倾斜,有比较好的效果

4、将reduce join转换为map join

普通reduce join原理图:

spark sql cross join 数据倾斜 spark 解决数据倾斜_数据倾斜_02

map join原理图:

spark sql cross join 数据倾斜 spark 解决数据倾斜_hive_03

普通的join

普通Join会走shuffle,即reduce join。先将所有相同的key对应的values,汇聚到一个task中,然后再进行join

reduce join转换为map join

如果两个RDD要进行join,其中一个RDD是比较小的。一个RDD是100万数据,一个RDD是1万数据,broadcast出去那个小RDD的数据以后,就会在每个executor的block manager中都驻留一份(须确保内存足够存放那个小RDD中的数据)

这种方式下不会发生shuffle操作,也不会发生数据倾斜;从根本上杜绝了join操作可能导致的数据倾斜的问题;对于join中有数据倾斜的情况,应第一时间考虑这种方式。

不适合的情况:

两个RDD都比较大,如果将其中一个broadcast,可能导致内存不足。

5、sample采样倾斜key进行两个jion

spark sql cross join 数据倾斜 spark 解决数据倾斜_数据_04

方案的实现思路:其关键之处在于将发生数据倾斜的key单独拉出来,放到一个RDD中去;就用这个原本会倾斜的key RDD跟其它RDD进行单独的join;key对应的数据,可能就会分散到多个task中去进行join操作,最后将join后的表进行union操作

应用场景:

针对RDD的数据,可以手动把它转换成一个中间表,或者直接用countByKey()的方式,查看该RDD各个key对应的数据量;此时如果发现整个RDD就一个,或者少数几个key对应的数据量特别多;尽量建议,比如就是一个key对应的数据量特别多,可采用该方案,拉取出来数据最多的key单独进行join,尽可能将key分散到各个task。

进一步优化:

将单独的一个或者少数几个可能产生数据倾斜的key拉取出来,还可以进一步优化。对于拉取出来的key,从另外一个要join的表中也过滤出一份数据,比如可能就只有一条数据。userid2infoRDD,一个userid key就对应一条数据,然后对该只有一条数据的RDD进行flatMap操作,打个100个随机数,作为前缀,返回100条数据。将数据进行打散再join,join完以后执行map操作,去掉之前打上的随机数,然后再和另外一个普通RDD join以后的结果,进行union操作。

不适用场景:

如果一个RDD中,导致数据倾斜的key特别多,那么将不适用此方式。

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

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

spark sql cross join 数据倾斜 spark 解决数据倾斜_数据倾斜_05

实现步骤:

  • 1)选择一个RDD,要用flatMap()进行扩容(比较小的RDD),将每条数据映射为多条数据,每个映射出来的数据,都带一个n以内的随机数,通常来说,会选择10以内。
  • 2)将另外一个RDD,做普通的map映射操作,每条数据都打一个10以内的随机数
  • 3)最后,将两个处理后的RDD进行join操作

另外一个方法:

sample采样倾斜key并单独进行join

  • 1)将key从另外一个RDD中过滤出的数据,可能只有一条或者几条,此时可以任意进行扩容,扩成1000倍
  • 2)将从第一个RDD中拆分出来的那个倾斜key RDD,打上1000以内的一个随机数
  • 3)join并且提供并行度。这样配合上,提升shuffle reduce并行度,join(rdd,1000)。通常情况下,效果还是非常不错的。打散成100份,甚至1000份、2000份去进行join,这样数据倾斜的问题肯定降低了。

此方法局限性:

  • 1)因为两个RDD都很大,因此没有办法将某一个RDD扩的特别大,一般就是扩10倍
  • 2)如果是10倍的话,那么数据倾斜的问题只能说是缓解和减轻,不能彻底解决