1:上图理解
1:Map阶段是在数据源通过InputFormat来设置通过什么样的一个方式来读取数据,比如之前的数据一行一行的方式读取,读取完之后交给Mapper,即用户编写的业务逻辑代码进行处理。
2:然后通过Shuffle把Mapper阶段的数据拉到Reducer阶段,此阶段可以对数据进行排序,分区,压缩,合并等功能。
3:Reducer处理完之后,通过OutputFormat可以对输出格式进行设置,比如设置把处理之后的数据写道HBase,数据库或者是文件。
2:切片与MapTask并行度决定机制
1:数据量比较大的时候,可以多开几个MapTask并行工作,提高工作效率,但是当数据量特别小的时候,就没有必要开太多的MapTask,因为处理数据的时间远比开启服务的时间少很多,效率较低。
2:数据块:Block是HDFS物理上把数据分为一块一块,数据块是HDFS存储数据单位。
3:数据切片:数据切片只是在逻辑上对输入进行分片,并不会在磁盘上将其切分成片进行存储。数据切片是MapReduce程序计算输入数据的单位,一个切片会对应启动一个MapTask。
3:Job提交阶段的源码分析
bool result = job.waitForCompletion(true);
System.exit(result ? 0 : 1)
进入waitForCompletion方法之后,通过state判断JobState.DEFINE,然后进入submit();
// ensureState(JobState.DEFINE) 还是判断状态是否一致问题
// setUserNewApi() 处理新旧Hadoop API的兼容性问题
// 1 建立连接 connect();
// 1)创建提交 Job 的代理,如果cluster为null,新建一个cluster对象
new Cluster(getConfiguration());
// (1)判断是本地运行环境还是 yarn 集群运行环境
initialize(jobTrackAddr, conf);
// 2 提交给新建集群的 job信息 submitter.submitJobInternal(Job.this, cluster)
// 1) checkSpaces(job)验证输出路径问题
// (1) output.checkOutputSpaces(job) 就是判断输出路径是否存在问题
// 2)创建给集群提交数据的 Stag 路径,当前路径是空的
Path jobStagingArea = JobSubmissionFiles.getStagingDir(cluster, conf);
// 3)获取 jobid ,并创建 Job 路径,提交每一个任务,都会有一个jobId
JobID jobId = submitClient.getNewJobID();
// 4)拷贝 jar 包到集群
copyAndConfigureFiles(job, submitJobDir);
rUploader.uploadFiles(job, jobSubmitDir);
// 这段代码执行完之后就会有之前的路径并加上指定JobId路径
mkdir(jtFs,submitJobDir,mapredSysPerms);
// 如果不是本地模式,需要把jar包提交到集群,如果是本地模式,不需要提交
upLoadJobJar(job,jobJar,submitJobDir,replication,statCache);
// 5)计算切片,生成切片规划文件
//方法执行,路径下有四个文件,job.split,job.splitmetainfo以及分别对应的crc文件
int maps = writeSplits(job, submitJobDir);
conf.setInt(MRJobConfig.NUM_MAPS,maps)//默认情况下设置MapTask个数与切片个数一致
// 6)向 Stag 路径写 XML 配置文件,执行完之后,路径多了两个文件,job.xml以及对应的crc文件,里面存放的一些参数设置的默认值,job执行所需的参数
writeConf(conf, submitJobFile);
conf.writeXml(out);
// 7)提交 Job,返回提交状态,然后status由DEFINE转为RUNNING
status = submitClient.submitJob(jobId,submitJobDir.toString(),job.getCredentials());
// 3 return isSuccessful()返回bool值
4:FileInputFormat源码分析
int maps = writeSplits(job, submitJobDir);
maps = writeNewSplits(job,jobSubmitDir)
1:List<InputSplit> splits = input.getSplits(job)
for(FileStatus file : files)// 对所传输的每个文件进行循环遍历操作,一个一个进行切片处理
if(isSplitable(job,path))// 该文件是否可切割,不可切割默认为一片
//计算切片大小
long spliteSize = computeSplitSize(blockSize,minSize,maxSize)
return Math.max(minSize,Math.min(maxSize,blockSize))
while(((double)byteRemaining/spliteSize)>SPLIT_SLOP)//SPLIT_SLOP为1.1,大于1.1即切割,小于等于就直接看成一片即可。
2:JobSplitWriter.createSpliteFiles(jobSubmitDir,conf,jobSubmitDir,
getFileSystem(conf),array)//这个执行完路径下才有切片信息
5:FileInputForamt切片机制
1:机制
1:简单的按照文件的内容长度切片
2:切片大小默认等于Block大小
3:切片不考虑数据集整体,而是逐一对每一个文件单独切片
2:案例
两个文件,file1.txt 250M file2.txt 10M
切割之后,file1.txt.split1 0-128M file1.txt.split2 129-250M file2.txt.split1 0-10M
6:常见的几种
1:FileInputFormat常见的接口实现类包括TextInputFormat,keyvalueInputFormat,NLineInputFormat,CombineTextInputFormat等等。用于处理多种数据格式,比如日志文件,二进制文件,数据库表等等。
2:TextInputFormat按行读取。
3:CombineTextInputFormat。因为采取TextInputFormat的时候,不管文件多小,都会产生一个单独的切片,交给一个MapTask,如果由大量的小文件时,就会产生大量的MapTask,效率低下。所以用于小文件过多的场景,它可以将多个小文件从逻辑上规划到 一个切片中,这样,多个小文件就可以交给一个 MapTask 处理。
4:CombineextInputFormat可通过CombineTextInputFormat.setMaxInputSplitSize(job, 4194304);// 4m设置虚拟存储切片大小
假设setMaxInputSplitSize为4M
虚拟存储过程 切片过程
a.txt 1.7M 1.7<4,划分一块 1:判断虚拟存储的文件大小是否
大于4M,大于等于则单独形成一个切片
b.txt 5.1M 5.1>4,但小于2*4M,划分两块,均为2.55M 2:如果不大于等于,则和下一个
虚拟存储文件合并,共同形成一个切片
c.txt 3.4M 3.4<4,一块
d.txt 6.8M 6.8>4,小于2*4M,两块,均为3.4M
最终存储文件
1.7 2.55 2.55 3.4 3.4 3.4
则最终形成三个切片,(1.7+2.55)M,(2.55+3.4)M,(3.4+3.4)M