
数据倾斜这四个字经常会在学习MapReduce中遇到。所谓数据分区,就是数据分区分布因为数据本身或者分区方法的原因变得极为不一致,大量的数据被划分到了同一个区。由于Reducer Task每次处理一个区的数据,这导致Reducer Task处理有着大量数据的分区时任务繁重,而其他区分到的任务过于轻松,从而导致整体的任务效率大幅降低。“一个人累死,其他人闲死”。
数据倾斜发生原理一般来说,数据倾斜会因为两种情况发生:
- 数据的key非常少,极少数的key中记录了非常多的记录值。这属于相同key分到同一个分区导致分区数据过多。
- 数据的key比较多,但有某些key的记录值远远多于其他key,在分区的时候将有着大量记录值的key分到了同一个区。这属于不同key因为分区方法分到同一个区导致分区数据过多。
- 大部分的Task运行速度很快,但是小部分Task运行速度很慢
- 原本能正常执行的Spark作业,某天突然爆出OOM(内存溢出)异常。观察异常栈,是我们写的业务代码造成的。
- 增加JVM的内存。这适用于第一种情况(数据的key非常少),往往只能通过硬件的手段来进行调优,增加jvm内存可以显著的提高运行效率。
- 增加Reduce的个数。这适用于第二种情况。第二种情况是因为有较多记录值的key都被分到了同一个分区,才导致了数据倾斜。如MapReduce,它的分区默认是HashPartitioner,让key的哈希值对设定的Reducer Task个数取余。如果我们增加Reduce的个数(修改numReduceTasks值),就会让一些key被分到不同的分区。虽然工作量仍然会不均衡,但是已不会有这么严重的数据倾斜。
public class HashPartitionerextends Partitioner { public int getPartition(K key, V value, int numReduceTasks) { return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks; } }
- 自定义分区。我们可以自定义一个分区类并继承partition类,自己编写分区策略,这种方法比较显著。
- 重新设计key。比如,我们可以在map阶段随机加上一个固定长度的随机数,使得分区的时候不会像之前那样分到同一个节点,在reduce阶段再去掉即可。
- 使用combiner合并。Map阶段会将环形缓冲区的数据排序并溢写,在溢写之前,使用combiner将相同key数据进行合并(如累加)。这减轻了数据倾斜的现象,减轻了map端向reduce端发送的数据量(减轻了网络带宽),也减轻了map端和reduce端中间的shuffle阶段的数据拉取数量(本地化磁盘IO速率),推荐使用这种方法。
参考:https://blog.csdn.net/weixin_35353187/article/details/84303518
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)