深入理解Mapreduce(shuffle机制、数据倾斜、切片机制)

1. MapReduce介绍

map:映射(键值对) 最小化数据单元,把每个单元以键值对的形式发送到下一个环节
reduce:减少,合并。把map端送过来的最小化数据,按照key相同,value值做运算
map阶段输出数据的形式:(key,value)
---------------- shuffle机制 -----------------------------
reduce阶段接收形式:(key,(value,value,value))

注意:map读取数据是按行读取,key是偏移量,value是每一行的数据

2. Suffle机制

2.1. suffle流程

为什么判断有suffle的存在?
答:mapper的输出 != reducer的输入,所以在mapper和reducer之间一定有一个suffle机制。

suffle存在于哪里?
答:存在于mapper之后,reducer之前,并且一部分存在于maptask,另一部分存在于reducetask,如图:
在这里插入图片描述
流程概括:
当mapper运行时,数据会流入环形缓冲区,当环形缓冲区的存储达到80%的时候,会发生溢写,溢写到磁盘的同时会经历三个过程(partition,sort,combine)
maptask运行结束后,reducetask会从maptask获取结果,并再次进行排序,分组,聚合把数据变成(key,Iterator)的形式传入reduecer执行。

2.2. Partition(分区)

在maptask的shuffle阶段,会发生分区,默认分区的数量与reduce的数量相等,每个reduce执行对应编号的partition,一对一。通常情况下是按照key分区,默认执行的分区类是HashPartitioner,其中的分区规则是
(hashCode & Integer.MAX_VAlUE)%reduceNumber进行分区,返回分区号。

2.3. sort(排序)

只有key会排序,被用作key的类必须同时实现Writable和WritableComparable接口

2.4. combiner(合并)

combiner阶段发生在map端,但是不是必须要的,当reduce端的压力过大,combiner会分担执行reduce的任务。

2.5. 分组

分组是在reduce端的shuffle中进行的。

  • 怎么理解分组?
    世界上本没有分组,就是按照某种规则进行排序,排到相等的时候,自然形成一个分组。

怎么实现分组:
a.默认类型
默认的排序规则就是默认的分组规则
b.自定义类型
必须写默认的排序规则,充当默认的分组规则

而自定义分组就是自定义的排序,只是这个自定义排序用在了分组上面。

2.5. 深挖环形缓冲区

环形缓冲区在底层是一个byte[] kvbuffer的数组

  • 环形缓冲区分为三块,空闲区、数据区、索引区。初始位置取名叫做“赤道”,调整环形缓冲区的大小:mapreduce.task.io.sort.mb,默认100M
  • 环形缓冲区写入的时候,有个细节:数据是从赤道的右边开始写入,索引(每次申请4kb)是从赤道是左边开始写。这个设计很有意思,这样两个文件各是各的,互不干涉。
  • 在数据和索引的大小到了mapreduce.map.sort.spill.percent参数设置的比例时(默认80%,这个是调优的参数),会有两个动作:
    1、对写入的数据进行原地排序,并把排序好的数据和索引spill到磁盘上去;
    2、在空闲的20%区域中,重新算一个新的赤道,然后在新赤道的右边写入数据,左边写入索引;
    3、当20%写满了,但是上一次80%的数据还没写到磁盘的时候,程序就会panding一下,等80%空间腾出来之后再继续写。
    如此循环往复,永不停歇,直到所有任务全部结束。整个操作都在内存,形状像一个环,所以才叫环形缓冲区。
  • 为什么要有缓冲区
    环形缓冲区不需要重新申请新的内存,始终用的都是这个内存空间。大家知道MR是用java写的,而Java有一个最讨厌的机制就是Full GC。Full GC总是会出来捣乱,这个bug也非常隐蔽,发现了也不好处理。环形缓冲区从头到尾都在用那一个内存,不断重复利用,因此完美的规避了Full GC导致的各种问题,同时也规避了频繁申请内存引发的其他问题。
    另外呢,环形缓冲区同时做了两件事情:1、排序;2、索引。在这里一次排序,将无序的数据变为有序,写磁盘的时候顺序写,读数据的时候顺序读,效率高非常多!
    在这里设置索引区也是为了能够持续的处理任务。每读取一段数据,就往索引文件里也写一段,这样在排序的时候能加快速度。

3. 数据倾斜

3.1. Reduce端的数据倾斜

当自定义partitioner分区不公平的时候,有可能会出现严重的数据倾斜,但是用默认的分区又不能实现全排序,那该用什么呢?
答:随机抽样生成分区文件,系统会读取那个分区文件进行分区。
用Random.Sampler类生成分区文件,再用TotalOrderPartitioner进行读取
在这里插入图片描述
在这里插入图片描述

3.2. reduce端压力过大

这是整个mapreduce的数据倾斜,map端只负责最小化数据单元,但是reduce端可能负责了上千万条数据的合并与计算,这就使reduce端的执行速度大大低于map端的执行速度,这个时候就需要在map端进行一些reduce的工作,来分担压力,这个的执行位置在map端,设置combiner执行。

4. 切片

4.1. 切片机制

输入格式化(前三个都是默认切片方式,每个文件按照块大小切片)

  1. TextInputFile
    最普通的文本文档格式,MR中默认使用的文件格式,不需要特殊指定
    LongWritable Text
  2. SequenceFileInputFormat
    序列化文件格式,文件中两个字段,分别代表key和value。
    KEYIN VALUEIN (根据文件字段的实际类型确定)
  3. KeyValueFileInputFormat
    是普通的文本文档,但是每行都有两个字段,分别代表key和value
    Text Text
    默认的分隔符是 \t 无需特别指定
    如果需要改变分隔符, job.getConfiguration().set(KeyValueLineRecordReader.KEY_VALUE_SEPERATOR,“xxx”)
  4. NLineInputFormat
    普通文本文档,用法与TextInputFormat一致
    打破原有的分片规则,按行分片
    默认按一行进行分片,可以使用
    NLineInputFormat.setNumLinesPerSplit(job,n);改变切片行数
  5. CombineTextInputFormat
    针对小文件处理的一种格式,为了避免每个小文件对应一个split,
    导致过多的maptask产生,且每个maptask中执行的内容特别少。
    步骤1:虚拟存储过程
    步骤2:切片
    在这里插入图片描述
  6. 自定义

5. mr的shuffle会有多少次排序

MapReduce Shuffle过程中总共发生3次排序,详细分别如下:

第一次排序行为:在map阶段,由环形缓冲区溢出到磁盘上时,落地磁盘的文件会按照key进行分区和排序,属于分区内有序,排序算法为快速排序

第二次排序行为:在map阶段,对溢出的文件进行combiner合并过程中,需要对溢出的小文件进行归档排序,合并,排序算法为归并排序.

第三次排序行为:在map阶段,reduce task将不同map task端文件拉取到同一个reduce分区后,对文件进行合并,排序,排序算法为归并排序

6. mr为什么要排序

方便后续的开发
就是MapReduce的原生实现中,通过sorting,不仅实现了分组,也保证了数据的分组内有序,虽然也有并不关心数据是否局部有序的应用场景,但在数据访问和计算中,保证数据有序性是一个必要功能。在通过sort实现数据分组的同时保证了分组内数据有序性,也算是一举两得,并且这样在要求数据有序性的场景中,对局部有序的数据再进行全局排序将会有不少帮助。

sort是为了通过外排(外部排序)降低内存的使用量:因为reduce阶段需要分组,将key相同的放在一起进行规约,使用了两种算法:hashmap和sort,如果在reduce阶段sort排序(内部排序),太消耗内存,而map阶段的输出是要溢写到磁盘的,在磁盘中外排可以对任意数据量分组(只要磁盘够大),所以,map端排序(shuffle阶段),是为了减轻reduce端排序的压力。

7. mapreduce运行机制

  1. Map阶段

    l 第一阶段是把输入目录下文件按照一定的标准逐个进行逻辑切片,形成切片规划。默认情况下,Split size = Block size。每一个切片由一个MapTask处理。(getSplits)

    l 第二阶段是对切片中的数据按照一定的规则解析成<key,value>对。默认规则是把每一行文本内容解析成键值对。key是每一行的起始位置(单位是字节),value是本行的文本内容。(TextInputFormat)

    l 第三阶段是调用Mapper类中的map方法。上阶段中每解析出来的一个<k,v>,调用一次map方法。每次调用map方法会输出零个或多个键值对。

    l 第四阶段是按照一定的规则对第三阶段输出的键值对进行分区。默认是只有一个区。分区的数量就是Reducer任务运行的数量。默认只有一个Reducer任务。

    l 第五阶段是对每个分区中的键值对进行排序。首先,按照键进行排序,对于键相同的键值对,按照值进行排序。比如三个键值对<2,2>、<1,3>、<2,1>,键和值分别是整数。那么排序后的结果是<1,3>、<2,1>、<2,2>。如果有第六阶段,那么进入第六阶段;如果没有,直接输出到文件中。

    l 第六阶段是对数据进行局部聚合处理,也就是combiner处理。键相等的键值对会调用一次reduce方法。经过这一阶段,数据量会减少。本阶段默认是没有的。

  2. reduce阶段

    l 第一阶段是Reducer任务会主动从Mapper任务复制其输出的键值对。Mapper任务可能会有很多,因此Reducer会复制多个Mapper的输出。

    l 第二阶段是把复制到Reducer本地数据,全部进行合并,即把分散的数据合并成一个大的数据。再对合并后的数据排序。

    l 第三阶段是对排序后的键值对调用reduce方法。键相等的键值对调用一次reduce方法,每次调用会产生零个或者多个键值对。最后把这些输出的键值对写入到HDFS文件中。

8. 使用MapReduce实现两表的join

map join
map side join 是针对一下场景进行的优化。两个待连接的表中,有一个表非常大,而另一个非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多份,让每一个map task内存中存在一份(比如放在hash table中),然后只扫描大表:对于大表中的每一条记录key/value,在hash table中查找是否具有相同key的记录,入股有,则连接后输出即可。
场景:MapJoin 适用于有一份数据较小的连接情况。

做法:直接将较小的数据加载到内存中,按照连接的关键字建立索引,大份数据作为MapTask的输入数据对 map()方法的每次输入都去内存当中直接去匹配连接。然后把连接结果按 key 输出,这种方法要使用 hadoop中的 DistributedCache 把小份数据分布到各个计算节点,每个 maptask 执行任务的节点都需要加载该数据到内存,并且按连接关键字建立索引。

reduce join
在map阶段,map函数同时读取两个文件File1和File2,为了区分两种来源key/value数据对,没条数据打一个标签(tag),比如:tag=0表示来自文件File1,tag=2表示来自文件File2。
在map阶段, 把关键字作为key输出,并在value中标记出数据是来自data1还是data2。因为在shuffle阶段已经自然按key分组,reduce阶段,判断每一个value是来自data1还是data2,在内部分成2组,做集合的乘积。

9. 小文件优化

很明显hadoop适合大文件的处理和存储,那为什么不适合小文件呢?

  1. 从存储方面来说:hadoop的存储每个文件都会在NameNode上记录元数据,如果同样大小的文件,文件很小的话,就会产生很多文件,造成NameNode的压力。
  2. 从读取方面来说:同样大小的文件分为很多小文件的话,会增加磁盘寻址次数,降低性能
  3. 从计算方面来说:我们知道一个map默认处理一个分片或者一个小文件,如果map的启动时间都比数据处理的时间还要长,那么就会造成性能低,而且在map端溢写磁盘的时候每一个map最终会产生reduce数量个数的中间结果,如果map数量特别多,就会造成临时文件很多,而且在reduce拉取数据的时候增加磁盘的IO。

好,我们明白小文件造成的弊端之后,那我们应该怎么处理这些小文件呢?

  1. 从源头干掉,也就是在hdfs上我们不存储小文件,也就是数据上传hdfs的时候我们就合并小文件
  2. 在FileInputFormat读取入数据的时候我们使用实现类CombineFileInputFormat读取数据,在读取数据的时候进行合并。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若能绽放光丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值