前言
南国在最开始学习Hadoop的时候,一直其他人说的数据倾斜及数据倾斜的解决办法没有完全弄明白。通过这段时间的学习,看了许多资料,这里参考网上资料 以及自己的理解。这篇博客 写一个有关于数据倾斜的归纳总结。话不多说,干货马上送到。。。
数据倾斜的定义
在弄清什么是数据倾斜之前,我想让大家看看数据分布的概念:
正常的数据分布理论上都是倾斜的,就是我们所说的20-80原理:80%的财富集中在20%的人手中, 80%的用户只使用20%的功能 , 20%的用户贡献了80%的访问量 , 不同的数据字段可能的
数据倾斜一般有两种情况:
- 一种是唯一值非常少,极少数值有非常多的记录值(唯一值少于几千)
- 一种是唯一值比较多,这个字段的某些值有远远多于其他值的记录数,但是它的占比也小于百分之一或千分之一
数据倾斜在MapReduce编程模型中十分常见,它是key分布不均匀,导致分发到不同的reduce上,个别reduce任务特别重,导致其他reduce都完成,而这些个别的reduce迟迟不完成的情况。用最通俗易懂的话来说,数据倾斜无非就是大量的相同key被partition分配到一个分区里,造成了’一个人累死,其他人闲死’的情况,这种情况是我们不能接受的,这也违背了并行计算的初衷,首先一个节点要承受着巨大的压力,而其他节点计算完毕后要一直等待这个忙碌的节点,也拖累了整体的计算时间,可以说效率是十分低下的。
在map端和reduce端都有可能发生数据倾斜。在map端的数据倾斜会让多样化的数据集的处理效率更低。在reduce端的数据倾斜常常来源于MapReduce的默认分区器。
分区:
常见的mapreduce分区方式为hash 和range ,
hash partition 的好处是比较弹性,跟数据类型无关,实现简单(设定reduce个数就好,一般不需要自己实现)
range partition 需要实现者自己了解数据分布, 有时候需要手工做sample取样. 同时也不够弹性, 表现在几个方面,
- 对同一个表的不同字段都需要实现不同的range partition, 对于时间这种字段根据查询类型的不同或者过滤条件的不同切分range 的大小都不一定.
- 有时候可能设计使用多个字段组合的情况, 这时候又不能使用之前单个字段的partition 类, 并且多个字段组合之间有可能有隐含的联系,比如出生日期和星座,商品和季节.
- 手工做sample 非常耗时间,需要使用者对查询使用的数据集的分布有领域知识.
- 分配方式是死的,reduce 个数是确定的,一旦某种情况下发生倾斜,调整参数
其他的分区类型还有hbase 的hregionpartitioner 或者totalorder partitioner 等.
数据倾斜的原因
发生数据倾斜的原因有:
- key分布不均匀。
- map端数据倾斜,输入文件太多且大小不一 。
- reduce端数据倾斜,分区器问题。
- 业务数据本身的特征。
数据倾斜的解决办法
解决方案:
- 1.增加jvm内存,这适用于第一种情况(唯一值非常少,极少数值有非常多的记录值(唯一值少于几千)),这种情况下,往往只能通过硬件的手段来进行调优,增加jvm内存可以显著的提高运行效率。
- 2.增加reduce的个数,这适用于第二种情况(唯一值比较多,这个字段的某些值有远远多于其他值的记录数,但是它的占比也小于百分之一或千分之一),我们知道,这种情况下,最容易造成的结果就是大量相同key被partition到一个分区,从而一个reduce执行了大量的工作,而如果我们增加了reduce的个数,这种情况相对来说会减轻很多,毕竟计算的节点多了,就算工作量还是不均匀的,那也要小很多。
- 3.自定义分区,这需要用户自己继承partition类,指定分区策略,这种方式效果比较显著。
- 4.重新设计key,有一种方案是在map阶段时给key加上一个随机数,有了随机数的key就不会被大量的分配到同一节点(小几率),待到reduce后再把随机数去掉即可。
- 5.使用combinner合并,combinner是在map阶段,reduce之前的一个中间阶段,在这个阶段可以选择性的把大量的相同key数据先进行一个合并,可以看做是local reduce,然后再交给reduce来处理,这样做的好处很多,即减轻了map端向reduce端发送的数据量(减轻了网络带宽),也减轻了map端和reduce端中间的shuffle阶段的数据拉取数量(本地化磁盘IO速率),推荐使用这种方法。
总结
注意:数据倾斜没有一劳永逸的方式可以解决,了解你的数据集的分布情况,然后了解你所使用计算框架的运行机制和瓶颈,针对特定的情况做特定的优化,做多种尝试,观察是否有效.