通常情况下,数据分布都存在倾斜,我们常说的20-80原理:80%的财富集中在20%的人手里,20%的用户贡献了80%的访问量。
要讲数据倾斜,需要先说明MapReduce的工作原理。
map task
程序会根据InputFormat将输入文件分割成splits,每个split会作为map task的输入,每个map task会有一个内存缓冲区,输入数据经过map阶段处理后的中间结果会写入到内存缓冲区,并且会决定写入到哪一个partition。当输入的数据达到内存缓冲区的阈值时(默认为0.8)会启动一个线程将内存缓冲区的数据持久化到磁盘,同时不影响map的中间结果数据继续写入缓冲区,在持久化的过程中,MapReduce框架会对key进行排序,如果中间结果比较大,则会形成多个溢写文件,最后的缓冲区数据也会全部溢写到磁盘(至少有一个溢写文件),如果是多个溢写文件,则最后合并所有的溢写文件为一个文件。
reduce task
当所有的map task完成后,每个map task会形成一个最终文件,并且该文件按区划分。reduce任务启动之前,一个map task完成后,就会启动线程来拉取map的结果数据到reduce task,不断的合并数据,为reduce的数据输入做准备,当所有的map task完成后,数据也拉取合并完毕后,reduce task启动,最终将输出结果存入HDFS。
数据倾斜发生的问题在于map task对key的排序,当大量相同的key分配到同一个partition时,造成一台机器高负荷,其他机器空闲,让单节点承受巨大的压力,整体上的效率低不说,容易产生单点宕机。因为大多数的时候,单机的性能是比较低的。针对数据倾斜,在mapreduce时有几种解决方法。
1、增加jvm内存
适用于极少数值有非常多 & 唯一值少的数据,此为小数据集的情况。
2、增加reduce个数
适用情况:唯一值比较多,某些值有远远多于其他值的记录数,但它的占比也小于1%或千分之一。这种情况容易造成大量相同的key被partition到同一个分区,从而一个reduce执行了大量的工作,如果增加reduce的个数,单个reduce的工作量就降低了,即使工作量不均匀,但也小许多。
3、自定义分区
需要用户自定义指定分区,需要针对单独的数据进行处理,制定对应的分区策略。
4、重新设计key
在map阶段给key加一个随机数,有了随机数后key被分配到同一个节点的几率就很小了,达到reduce后再把随机数去掉。
5、使用combinner合并
combinner是在map阶段,reduce之前的一个中间阶段,这个阶段可以选择性的把大量的相同的key数据先进行一个合并,可以看做是local reduce,然后再交给reduce来处理,这样既减轻了map端到reduce端发送的数据量,也减轻了map端和reduce端中间的shuffle阶段数据拉取的数量(本地化磁盘IO速率),优先推荐使用该方法。
网友评论