1  什么是数据倾斜

数据倾斜是指某些任务对应分区上的数据显著多于其他任务对应分区上的数据,从而导致这部分分区上数据的处理速度成为处理整个数据集的瓶颈。

在Spark中,同一Stage内不同的任务可以并行执行,而不同Stage之间的任务可以串行执行。如图所示,假设一个Spark作业分为Stage 0和Stage 1,且Stage 1依赖于Stage 0,那么在Stage 0完全处理结束之前系统不会处理Stage 1。Stage 0包含4个任务。其中,任务1有10 000条数据,5s完成;任务2有12 000条数据,4s完成;任务3有7 000条数据,3s完成;任务4有100 000条数据,60s完成。这说明Stage 0的任务4上发生了数据倾斜,Stage 0的总运行时间至少为60s。也就是说,一个Stage耗费的时间主要由最慢的那个任务决定,由于同一Stage内的所有任务都具有相同的计算逻辑,而且每个任务的CPU和内存数量都是相同的,因此在排除不同计算节点之间计算能力差异的前提下,不同任务之间耗时的差异主要由任务处理的数据量决定。

hadoop数据倾斜统计大量数据优化 数据倾斜spark_大数据

图  Spark数据倾斜

2  导致数据倾斜的原因是什么

 

数据倾斜的原因很简单:将数据分配给不同的任务一般是在Shuffle过程中完成的,在Shuffle操作中,键相同的数据一般会被分配到同一分区上并交给某个任务处理,当某个键的数量太大时(远远大于其他键的数量),就会导致数据倾斜,如图所示。数据倾斜最致命的问题就是内存溢出(OOM)。

hadoop数据倾斜统计大量数据优化 数据倾斜spark_spark_02

 

图 导致数据倾斜的原因

3  如何判断Spark应用程序在运行中出现了数据倾斜

为了处理数据倾斜问题,我们需要找到数据倾斜的原因。判断数据倾斜的常用方法如下。

· 使用Spark Web UI观察任务执行时间:通过Spark Web UI,我们可以查询到每个任务处理的数据量大小和需要的执行时间。如果某个任务处理的数据量和需要的执行时间都明显多于其他任务,就说明很可能出现了数据倾斜。

· 统计每个分区上的数据量:出现数据倾斜的判断依据就是每个分区上的数据量。因此,若通过日志输出每个分区上的数据量并分析出现数据倾斜的分区,然后统计并输出发生数据倾斜的分区上键的分布,就能直接定位数据倾斜的原因。对问题定位并处理后,再关闭对每个分区上数据量的统计。

· 分析键的分布:使用Spark SQL或Presto这样的查询引擎可统计将要计算的数据中键的分布情况,进而判断是否发生了数据倾斜。

· 审查代码:重点查看join、groupByKey、reduceByKey等操作的关键代码,看看是否某些关键计算导致了数据倾斜。

4  数据倾斜消除方案

1.对源数据进行聚合并过滤导致数据倾斜的键

根据键对数据进行统计,找出导致数据倾斜的键。如果导致数据倾斜的键不影响业务的计算,就将这些键(例如−1、null等)过滤掉,这种情况可能导致数据倾斜。另外,诸如−1或null这样的键是“脏数据”,使用filter算子可将这些导致数据倾斜的键过滤掉,这样其他的键就有了均衡的值,从而消除了数据倾斜。

如果在Spark SQL查询中发生了数据倾斜,那么在Spark SQL中使用where子句可以过滤导致数据倾斜的键,之后再执行groupBy等表关联操作。

如图所示,假设需要按照city对数据进行分组并统计user表中的Top 3城市,city=null的数据有10万条之多,这会导致数据倾斜。由于null表示不知道user数据来自哪个城市,其统计值对业务来说毫无意义,因此只需要将null值过滤掉,即可很好地解决数据倾斜问题。

hadoop数据倾斜统计大量数据优化 数据倾斜spark_spark_03

图  过滤导致数据倾斜的数据

 

2.使用随机数消除数据倾斜

当导致数据倾斜的键对应的数据为有效数据时,我们就不能简单地对数据进行过滤了,可以通过为倾斜的键加上随机数来消除数据倾斜。过程如下:针对键执行map()函数,加上随机数,计算出结果后,再次针对键执行map()函数,将随机数去掉,这样计算得到的结果和不加随机数时计算得到的结果是一样的,但好处在于可以将数据均等分配到多个任务中并进行计算。

对于图6-19所示的任务,在对任务1中的数据进行处理时,如果分区策略为哈希策略,那么键为1的数据会被分配到一个任务中,键为2的数据则会被分配到另一个任务中。由于键为1的数据有3条,但键为2的数据只有1条,因此会发生数据倾斜。如果在处理数据时,先通过map()函数为任务1中数据的每个键加上一个随机的前缀,再执行数据处理操作,那么任务1中的数据会被平均分配到任务2.1和任务2.2中,这样就可以很好地解决数据倾斜问题,如图所示。数据处理操作完成后,再次利用map()函数可去掉之前加上的前缀,这样既保证了数据计算的准确性,又消除了数据倾斜,使任务最终得以快速运行完。

hadoop数据倾斜统计大量数据优化 数据倾斜spark_hadoop_04

 

图 使用随机数消除数据倾斜
 

 

 想要更全面了解Spark内核和应用实战,可以购买我的新书。