MapReduce框架原理之InputFormat的数据输入
InputFormat数据输入

切片与MapReduce并行度决定机制
问题引出
MapTask的并行度决定Map阶段的任务处理并发度,进而影响到整个Job的处理速度。
思考:1G的数据,启动8个MapTask,可以提高集群的并发处理能力。那么1K的数据,启动8个MapTask,会提高集群性能吗?MapTask并行任务是否越多越好呢?那些因素影响了MapTask并行度?
MapTask并行度决定机制
数据块:Block是HDFS物理把数据分成一块一块。数据块是HDFS存储数据单位。
数据切片:数据切片只是逻辑上对输入数据进行分片,并不会在磁盘上将其切分成片进行存储。数据切片是MapReduce程序计算输入数据的单位,一个切片会对应启动一个MapTask

如图所示:
逻辑切片为100M的时候,虽然启动了三个MapTask但是,会存在不完整数据,需要在不同的节点之间进行传输数据,效率会比较低 当逻辑切片为128M等于默认值BlockSize的时候,此时三个MapTask全部在本地执行,不会存在数据传输的问题
Job提交流程源码和切片源码详解
Job提交流程源码详解
waitForCompletion()
submit();
// 1建立连接
connect();
// 1)创建提交Job的代理
new Cluster(getConfiguration());
// (1)判断是本地运行环境还是yarn集群运行环境
initialize(jobTrackAddr, conf);
// 2 提交job
submitter.submitJobInternal(Job.this, cluster)
// 1)创建给集群提交数据的Stag路径
Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);
// 2)获取jobid ,并创建Job路径
JobID jobId = submitClient.getNewJobID();
// 3)拷贝jar包到集群
copyAndConfigureFiles(job, submitJobDir);
rUploader.uploadFiles(job, jobSubmitDir);
// 4)计算切片,生成切片规划文件
writeSplits(job, submitJobDir);
maps = writeNewSplits(job, jobSubmitDir);
input.getSplits(job);
// 5)向Stag路径写XML配置文件
writeConf(conf, submitJobFile);
conf.writeXml(out);
// 6)提交Job,返回提交状态
status = submitClient.submitJob(jobId, submitJobDir.toString(), job.getCredentials());
Job提交流程源码解析 FileInputFormat 切片源码解析(input.getSplits(job))
程序先找到你数据存储的目录
开始遍历处理(规划切片)目录下的每一个文件
遍历第一个文件
ss.txt获取文件大小fs.sizeOf(ss.txt)
计算切片大小
computeSplitSize(Math.max(minSize,Max.min(maxSize,blocksize)))=blocksize=128M
默认情况下,切片大小=blocksize
开始切,形成第一个切片
ss.txt-0:128M 第二个切片ss.txt-128:256M 第三个切片ss.txt-256M:300M每次切片时,都要判断切完剩下的部分是否大于块的1.1倍,不大于1.1倍就划分一块切片
将切片信息写到一个切片规划文件中
整个切片的核心过程在
getSplit()方法中完成InputSplit只记录了切片的元数据信息,比如起始位置,长度以及所在的节点列表等
提交切片规划文件到YARN上,YARN上的MrAppMaster就可以根据切片规划文件开始计算MapTask的个数
FileInputFormat 切片机制
切片机制
简单地按照文件的内容长度进行切片 切片大小,默认等于Block大小 切片时不考虑数据集整体,而是逐个针对每一个文件单独切片
案例分析
输入数据有两个文件:
file1.txt 320M
file2.txt 10M
经过FileInputFormat的切片机制进行运算后,形成的切片信息如下:
file1.txt.split1-- 0~128
file1.txt.split2-- 128~256
file1.txt.split-- 256~320
file2.txt.split1-- 0~10M
源码中计算切片大小的公式
Math.max(minSize,Math.min(maxSize,blockSize));
mapreduce.input.fileinputformat.split.minsize=1 默认值为1
mapreduce.input.fileinputformat.splitmaxsize=Long.MAXValue 默认值为Long.MAXValue
因此,默认情况下,切片大小=blocksize
切片大小设置
maxsize(切片最大值):参数如果调的比blocksize小,则会让切片变小,而且就等于配置这个参数的值
minsize(切片最大值):参数如果把blocksize大,则可以让切片变得比blocksize还大
获取信息API
//获取切片的文件名称
String name = inputSplit.getPath().getName();
//根据文件类型获取切片信息
FileSplit inputSplit = (FileSplit) context.getInputSplit()Context 是MapReduce任务运行的一个上下文,包含了整个任务的全部信息
TextInputFormat
FileInputFormat实现类
思考:在运行MapReduce程序时,输入的文件格式包括:基于行的日志文件、二进制文件、数据库表等。那么,针对不同的数据类型,MapReduce是如何读取这些数据的呢?
FileInputFormat常见的接口实现类包括:TextInputFormat、KeyValueTextInputFormat 、NLineInputFormat、CombineTextInputFormat和自定义InputFormat等。
TextInputFormat
TextInputFormat是默认的FileInputFormat实现类。每行读取每条记录。键是存储在该行在整个文件中的起始字节偏移量,LongWritable类型。值是这行的内容,不包括任何行终止执行符(换行符和回车符),Text类型。
以下是一个示例,比如,一个切片包含了如何四条记录
Rich learning form
Intelligent learning engine
Learning more convenient
From the real demand for more close to the enterprise
每条记录表示为以下键/值对
(0,Rich learning form)
(20,Intelligent learning engine)
(49,Learning more convenient)
(74,From the real demand for more close to the enterprise)
CombineTextInputFormat 切片机制
框架默认的TextInputFormat切片机制是对任务按文件规划切片,不管文件多小,都会开始一个单独的切片,都会交给一个MapTask,如果这样有大量小文件,就会产生大量的MapTask,处理效率及其低下。
应用场景
CombineTextInputFormat用于小文件过多的场景,他可以将多个小文件从逻辑规划到一个切片中,这样,多个小文件就可以交给一个MapTask处理。
虚拟切片最大值设置
CombineTextInputFormat.setMaxInputSplit(job,4194304); //4m
注意:虚拟存储切片最大值设置最好根据实际的小文件大小情况来设置具体的值
切片机制
生成切片过程包括:虚拟存储过程和切片过程两部分

combineTextInput切片机制 虚拟存储过程
将输入目录下所有文件大小,依次和设置的setMaxInputSplitSize值比较,如果不大于设置的最大值,逻辑上划分一个块。如果输入文件大于设置的最大值且大于两倍,那么以最大值切割一块;当剩余数据大小超过设置的最大值且不大于最大值2倍,此时将文件均分成2个虚拟存储块(防止出现太小切片)。
例如setMaxInputSplitSize值为4M,输入文件大小为8.02M,则先逻辑上分成一个4M。剩余的大小为4.02M,如果按照4M逻辑划分,就会出现0.02M的小的虚拟存储文件,所以将剩余的4.02M文件切分成(2.01M和2.01M)两个文件。
切片过程:
(a)判断虚拟存储的文件大小是否大于setMaxInputSplitSize值,大于等于则单独形成一个切片。
(b)如果不大于则跟下一个虚拟存储文件进行合并,共同形成一个切片。
(c)测试举例:有4个小文件大小分别为1.7M、5.1M、3.4M以及6.8M这四个小文件,则虚拟存储之后形成6个文件块,大小分别为:
1.7M,(2.55M、2.55M),3.4M以及(3.4M、3.4M)
最终会形成3个切片,大小分别为:
(1.7+2.55)M,(2.55+3.4)M,(3.4+3.4)M
注意文件的排序此时是和切片息息相关的
CombineTextInputFormat案例实操
需求
将输入的大量小文件合并成一个切片统一处理。
输入数据
期望
期望一个切片处理4个文件
实现过程
不做任何处理,继续运行wordCount案例程序,观察切片个数
number of splits:4
在WordcountDriver中增加如下代码,运行程序,并观察运行的切片个数为3。
驱动类中添加代码如下:
// 如果不设置InputFormat,它默认用的是TextInputFormat.class
job.setInputFormatClass(CombineTextInputFormat.class);
//虚拟存储切片最大值设置4m
CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);
运行结果为3个切片
number of splits:3
在WordcountDriver中增加如下代码,运行程序,并观察运行的切片个数为1。
驱动中添加代码如下:
// 如果不设置InputFormat,它默认用的是TextInputFormat.class
job.setInputFormatClass(CombineTextInputFormat.class);
//虚拟存储切片最大值设置20m
CombineTextInputFormat.setMaxInputSplitSize(job, 20971520);
运行结果为1个切片
number of splits:1
MapReduce 工作流程


上面的流程就是整个MapReduce最全工作流程,但是Shuffle过程只是从第7步到第16步结束,具体Shuffle过程详解,如下:
MapTask收集我们的map()方法输出的kv对,放到内存缓冲区中 从内存缓冲区中不断溢出本地磁盘文件,可能会溢出多个文件 多个溢出文件会被合成最大的溢出文件 在溢出过程及合并的过程中,都要调用Partitioner进行分区和针对key进行排序 ReduceTask根据自己的分区号,去各个MapTask机器上取相应的结果分区数据 ReduceTask会抓取到同一个分区的来自不同MapTask的结果文件,ReduceTask会将这些文件再进行合并(归并排序) 合并成大文件后,Shuffle的过程也就结束了,后面进入ReduceTask的逻辑运算过程(从文件中取出一个一个的键值对Group,调用用户自定义的reduce()方法) 注意:
Shuffle中的缓冲区大小会影响到MapReduce程序的执行效率,原则上说,缓冲区越大,磁盘io的次数越少,执行速度就越快。 缓冲区的大小可以通过参数调整,参数:mapreduce.task.io.sort.mb默认100M。 RecordReader:其作用就是将数据切分成key/value的形式然后作为输入传给Mapper。 好文一篇:MapReduce中Combiner的作用 <key,valuelist>并非是在shuff过程最终的结果,而是Reduce任务在Map结束后拿到的结果 归并排序适合已经排序好的数据

浙公网安备 33010602011771号