MapReduce技术
一、MapReduce工作原理 - 基本概念

·yarn操作系统基本组成 Resource Manager(资源调度)、Application Manager(任务调度)、NodeManage(对datanode计算资源)和
container(消息交互的资源描述)。
·client(客户端)向yarn提交job(作业),Resource Manager和Application Manager共同启动App Master(一个job对应一个App Master),
MRApp Master是App Master的一种实现。一个MRApp Master和Node manager管理多个Task (Map Task和Reduce Task)。
import java.io.IOException; import java.net.URI; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapred.TextInputFormat; import org.apache.hadoop.mapreduce.InputSplit; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.FileSplit; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class WordCount {
public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, WordCount.class.getSimpleName()); job.setJarByClass(WordCount.class); job.setMapperClass(MyMapper.class); job.setCombinerClass(MyReducer.class); job.setPartitionerClass(MyPartitioner.class); job.setReducerClass(MyReducer.class); job.setNumReduceTasks(4); //分区数为4 job.setOutputKeyClass(Text.class); job.setOutputValueClass(LongWritable.class); String uri="hdfs://node:9000/test/WordCount"; FileSystem fs = FileSystem.get(URI.create(uri), conf); FileStatus[] inputFiles = fs.listStatus(new Path(uri)); for (int i = 1; i < inputFiles.length; ++i) FileInputFormat.addInputPath(job, inputFiles[i].getPath()); FileOutputFormat.setOutputPath(job, new Path( "hdfs://node:9000/result/WordCount1" )); // if(job.waitForCompletion(true))System.out.println("OK"); } public static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{ Text k2 = new Text(); LongWritable v2 = new LongWritable(1); @Override protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException { // FileSplit fileSplit = (FileSplit) context.getInputSplit(); // System.out.println(fileSplit.getPath().getName()); //文件名 String[] split = value.toString().split(" "); for (String word : split) { k2.set(word); context.write(k2, v2); } } } public static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{ LongWritable v3=new LongWritable(); @Override protected void reduce(Text k2, Iterable<LongWritable> v2s,Context context) throws IOException, InterruptedException { long sum = 0; for (LongWritable one : v2s) sum+=one.get(); v3.set(sum); context.write(k2, v3); } } public static class MyPartitioner extends Partitioner<Text, LongWritable> { @Override public int getPartition(Text k, LongWritable v, int numPartitions) { ch=k.toString()[0] if(Character.isLetter(ch)) return ((int)Character.toLowerCase(ch)-97)/9; else return 3; } } }
二、MapReduce源数据流
·指定IO文件路径:FileInputFormat.addInputPath(JobConf conf, Path path); setInputPaths(JobConf conf, String commaSeparatedPaths)
·分片。把源文件分成一系列的InputSplit,每个inputSplit交由一个Mapper处理。
分片的大小splitSize=max{minSize, min{maxSize, blockSize}}。minSize、maxSize和blockSize默认大小为1MB、10MB和64MB;
blockSize文件块在hdfs-site.xml中设置,因为对网络传输的开销,blockSize比windows下的磁盘块大多了。
splitSize是逻辑分片,blockSize是物理分块:对一个大文件设置splitSize > blockSize,Mapper就需要从多个dataNode获取一个
完整的分片; 但是,如果文件实在太大,造就成千上万个分片会给集群带来很大的压力,这时就有必要设置splitSize > blockSize。
TextInputFormat.setMinInputSplitSize(job, 1); TextInputFormat.setMaxInputSplitSize(job, 1024 * 1024 * 10L); // 设置为10MB
·数据格式化。设置源文件中数据读取格式InputFormat、输出文件的数据格式OutFormat。InputFormat的接口实现有
TextInputformat(默认)、KeyValueTextInputFormat、SequenceFileInputFormat、NLineInputFormat、CombineFileInputFormat
对于TextInputFormat把输入文本逐行送入mapper函数处理,其中该行的起始偏移量作为Key,该行的内容为Value。
对于KeyValueTextInputFormat 默认以\t分隔key, value,在Mapper函数中设置k v类型。使用:参考示例
import org.apache.hadoop.mapreduce.lib.input.KeyValueTextInputFormat
conf.set("mapreduce.input.keyvaluelinerecordreader.key.value.separator", "#"); // 以#分隔
job.setInputFormatClass(KeyValueTextInputFormat.class);
对于NLineInputFormat 使用: 参考示例
NLineInputFormat.setNumLinesPerSplit(job, 4); //每4行一个split job.setMapperClass(NLineInputFormatMapper.class); job.setReducerClass(NLineInputFormatReducer.class);
三、Mapreduce过程 (参数调优)

·mapper把接受的<key,value>处理成<k,v>,mapper端的Shuffle过程把<k,v>进一步优化成reducer的直接输入数据。
·每一个mapper任务在内存中有一个输出缓存(默认100MB)和一个写入阈值(默认0.8)。
依据k值进行sort、combine和write(spill操作写入磁盘),结果生成多个spill文件,内部有序且合并为<k, v.list>。
·reduce的shuffle端用http协议,把spill从硬盘复制到JVM堆内存中,默认http线程数为5、内存阈值为0.7;
对每个spill通过归并排序把它变成全局有序,并依据k值存放于若干partition中。
·reducer把接受的<k, v.list>处理成新的<k,v>,OutputForamt输出[key \t value]。
因为中间有磁盘IO操作,有必要做数据压缩;设置sort比较策略、分区策略、分区大小:
job.setSortComparatorClass(DefineCompparator.class); //设置自定义的比较策略 job.setPartitionerClass(DefinedPartition.class); job.setNumReduceTasks(1); //job.setGroupingComparatorClass(DefinedGroupSort.class); //设置自定义的分组策略 job.getConfiguration().setBoolean("mapred.output.compress", true); // 设置自定义的压缩格式 Bzip2Codec.class job.getConfiguration().setClass("mapred.output.compression.codec", Bzip2Codec.class, CompressionCodec.class);
Hadoop Streaming使java外的其他语言实现MapReduce。如shell脚本
#! /bin/sh # mapper.sh while read LINE; do for word in $LINE do echo "$word 1" done done #! /bin/sh # reducer.sh count=0;started=0;word="" while read LINE;do newword=`echo $LINE | cut -d ' ' -f 1` if [ "$word" != "$newword" ];then [ $started -ne 0 ] && echo -e "$word\t$count" word=$newword count=1 started=1 else count=$(( $count + 1 )) fi done echo -e "$word\t$count"
测试:
2020-06-10 08:34:19

浙公网安备 33010602011771号