数据倾斜

306 阅读6分钟

什么是数据分布

正常的数据都是分布不均匀的,比如标准正态分布,多数集中在中间,少数分布在两边,这是一种正常状态。
就像我们所说的20-80原理:
80%的财富集中在20%的人手中, 
80%的用户只使用20%的功能,
20%的用户贡献了80%的访问量

什么是数据倾斜

<1>数据倾斜在MapReduce编程模型中十分常见,简单说就是:
    就是大量的相同key被partition分配到一个分区里,map/reduce程序执行时,
    reduce节点大部分执行完毕,但是有一个或者几个reduce节点运行很慢,导致整个程序的处理时间很长.
    这是因为某一个key的条数比其他key多很多(有时是百倍或者千倍之多),这条key所在的reduce节点所处理的数据量比其他节点就大很多,从而导致某几个节点迟迟运行不完。
    
<2>造成了'一个人累死,其他人闲死'的情况,这种情况是我们不能接受的,这也违背了并行计算的初衷,
<3>有的节点要承受巨大的计算压力,而有的节点的计算压力则很小,
<4>这样计算压力的节点在计算完之后,则要一只等着其他的节点全部计算完,也拖累了整体的计算时间,可以说效率是十分低下的
<5>举个简单的例子:
     拿word count这个入门的例子来说
     map阶段就是形成(“aaa”,1)的这样的kv形式,然后相同的key在同一个reduce中,在reduce阶段进行value相加,得出“aaa”出现次数。
     若进行word count的文本有100G,其中80G全部是“aaa”剩下20G是其余单词,
     那就会形成80G的数据量交给一个reduce进行相加计算,其余20G根据key不同分散到不同reduce进行相加计算的情况。
     如此就造成了数据倾斜,临床反应就是reduce跑到99%然后一直在原地等着那80G的reduce跑完。
     数据经过map后,由于不同key的数据量分布不均,在shuffle阶段中通过partition将相同的key 的数据打上发往同一个reducer的标记,
     然后开始spill溢写写入磁盘,最后merge成最终map阶段输出文件。
     如此一来80G的aaa将发往同一个reducer由此就可以知道reduce最后1%的工作在等什么了。

数据倾斜与业务逻辑和数据量有关

为什么说数据倾斜与业务逻辑和数据量有关??
从另外角度看数据倾斜,其本质还是在单个节点在执行那一部分数据reduce任务的时候,由于数据量大,跑不动,造成任务卡住。
若是这台节点机器内存够大,CPU、网络等资源充足,跑80G的数据量和跑10M数据所耗时间差距不大,
那么也就不存在问题,倾斜就倾斜吧,反正机器跑的动。
所以机器配置和数据量存在一个合理的比例,一旦数据量远超机器的极限,那么不管每个key的数据如何分布,总会有一个key的数据量超出机器的能力,造成reduce缓慢甚至卡顿。
   

数据倾斜的影响和危害

数据倾斜会极大影响性能和效率,导致计算性能低下,违背了并行计算的初衷。
当出现数据倾斜时,小量任务耗时远高于其它任务,从而使得整体耗时过大,
另外,当发生数据倾斜时,部分任务处理的数据量过大,往小了说会耗时,往大了说可能会因为内存不足使得任务失败,并进而引进整个应用失败。

数据倾斜的现象

当发现如下现象时,十有八九是发生数据倾斜了:
  <1>绝大多数reduce执行得都非常快,但个别reduce执行极慢,整体任务卡在某个阶段不能结束。
  <2>原本能够正常执行的Spark作业,某天突然报出OOM(内存溢出)异常,观察异常栈,是我们写的业务代码造成的。这种情况比较少见。
 
 

解决方案

<1>增加jvm内存

  这种解决方法适用于极少数的key的条数很多,
  这种情况,往往只能通过硬件的手段来进行调优,增加jvm内存可以显著的提高运行效率。   

<2>增加reduce的个数

  大量相同key被partition到一个分区,从而一个reduce执行了大量的工作,
  而如果我们增加了reduce的个数,这种情况相对来说会减轻很多。
  毕竟计算的节点多了,就算工作量还是不均匀的,计算速度也会比之前快很多。 

<3>自定义分区

  这需要用户自己继承partition类,指定分区策略,这种方式效果比较显著。   

<4>重新设计key

  有一种方案是在map阶段时给key加上一个随机数,有了随机数的key机会会当成不同的key分到不同的分区,
  进行一次reduce之后数据量会变少,
  然后把随机数去掉,再进行一次reduce。

<5>使用combinner合并

  combinner是在map阶段,reduce之前的一个中间阶段,
  在这个阶段可以选择性的把大量的相同key数据先进行一个合并,可以看做是local reduce,
  然后再进行分区,交给reduce节点来处理,
  这样做的好处很多,即减轻了map端向reduce端发送的数据量(减轻了网络带宽),
  也减轻了map端和reduce端中间的shuffle阶段的数据拉取数量(本地化磁盘IO速率),推荐使用这种方法。 
  

<6>参数调优

  Hadoop和Spark都自带了很多的参数和机制来调节数据倾斜,合理利用它们就能解决大部分问题。
    

<7>过滤异常数据

   如果导致数据倾斜的key是异常数据,那么简单的过滤掉就可以了。
   首先要对key进行分析,判断是哪些key造成数据倾斜。
   具体方法上面已经介绍过了,这里不赘述。
   然后对这些key对应的记录进行分析:
        空值或者异常值之类的,大多是这个原因引起
        无效数据,大量重复的测试数据或是对结果影响不大的有效数据
        有效数据,业务导致的正常数据分布
   解决方案:
        对于第 12 种情况,直接对数据进行过滤即可。
        第3种情况则需要特殊的处理,具体我们下面详细介绍。