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的算子:如groupByKey、countByKey、reduceByKey、join
(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
或者是,对每个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 实际生产环境中的经验
那么,如果出现第二种情况的话,就立即放弃这种方法,开始去尝试和选择后面的方法解决。
3、随机key实现双重聚合
原理介绍:
具体操作:
该方法对groupByKey、reduceByKey造成的数据倾斜,有比较好的效果
4、将reduce join转换为map join
普通reduce join原理图:
map join原理图:
普通的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
方案的实现思路:其关键之处在于将发生数据倾斜的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解决数据倾斜的时候,就代表之前的解决方案都失效。这个方案是没办法彻底解决数据倾斜的,更多的是一种对数据倾斜的缓解
实现步骤:
另外一个方法:
sample采样倾斜key并单独进行join
此方法局限性: