combiner筆記

每一個map可能會產生大量的輸出,combiner的作用就是在map端對輸出先做一次合併,以減少傳輸到reducer的數據量。

combiner最基本是實現本地key的歸併,combiner具有類似本地的reduce功能。

如果不用combiner,那麼,所有的結果都是reduce完成,效率會相對低下。使用combiner,先完成的map會在本地聚合,提升速度。

注意:Combiner的輸出是Reducer的輸入,Combiner絕不能改變最終的計算結果。所以從我的想法來看,Combiner只應該用於那種Reduce的輸入key/value與輸出key/value類型完全一致,且不影響最終結果的場景。比如累加,最大值等。


實現combiner


package combine;

import java.net.URI;

import org.apache.hadoop.conf.Configuration;
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.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.output.FileOutputFormat;


public class WordCountApp {
	static final String INPUT_PATH = "hdfs://hadoop:9000/hello";
	static final String OUT_PATH = "hdfs://hadoop:9000/out_hello";
	
	public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		final FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH), conf);
		final Path outPath = new Path(OUT_PATH);
		if(fileSystem.exists(outPath)){
			fileSystem.delete(outPath, true);
		}
		
		final Job job = new Job(conf , WordCountApp.class.getSimpleName());
		//1.1指定讀取的文件位於哪裏
		FileInputFormat.setInputPaths(job, INPUT_PATH);
		//指定如何對輸入文件進行格式化,把輸入文件每一行解析成鍵值對
		//job.setInputFormatClass(TextInputFormat.class);
		
		//1.2 指定自定義的map類
		job.setMapperClass(MyMapper.class);
		//map輸出的<k,v>類型。如果<k3,v3>的類型與<k2,v2>類型一致,則可以省略
		//job.setMapOutputKeyClass(Text.class);
		//job.setMapOutputValueClass(LongWritable.class);
		
		//1.3 分區
		//job.setPartitionerClass(HashPartitioner.class);
		//有一個reduce任務運行
		//job.setNumReduceTasks(1);
		
		//1.4 TODO 排序、分組
		
		//1.5 規約
		job.setCombinerClass(MyCombiner.class);
		
		//2.2 指定自定義reduce類
		job.setReducerClass(MyReducer.class);
		//指定reduce的輸出類型
		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(LongWritable.class);
		
		//2.3 指定寫出到哪裏
		FileOutputFormat.setOutputPath(job, outPath);
		//指定輸出文件的格式化類
		//job.setOutputFormatClass(TextOutputFormat.class);
		
		//把job提交給JobTracker運行
		job.waitForCompletion(true);
	}
	
	/**
	 * KEYIN	即k1		表示行的偏移量
	 * VALUEIN	即v1		表示行文本內容
	 * KEYOUT	即k2		表示行中出現的單詞
	 * VALUEOUT	即v2		表示行中出現的單詞的次數,固定值1
	 */
	static class MyMapper extends Mapper<LongWritable, Text, Text, LongWritable>{
		protected void map(LongWritable k1, Text v1, Context context) throws java.io.IOException ,InterruptedException {
			final String[] splited = v1.toString().split("\t");
			for (String word : splited) {
				context.write(new Text(word), new LongWritable(1));
				System.out.println("Mapper輸出<"+word+","+1+">");
			}
		};
	}
	
	/**
	 * KEYIN	即k2		表示行中出現的單詞
	 * VALUEIN	即v2		表示行中出現的單詞的次數
	 * KEYOUT	即k3		表示文本中出現的不同單詞
	 * VALUEOUT	即v3		表示文本中出現的不同單詞的總次數
	 *
	 */
	static class MyReducer extends Reducer<Text, LongWritable, Text, LongWritable>{
		protected void reduce(Text k2, java.lang.Iterable<LongWritable> v2s, Context ctx) throws java.io.IOException ,InterruptedException {
			//顯示次數表示redcue函數被調用了多少次,表示k2有多少個分組
			System.out.println("MyReducer輸入分組<"+k2.toString()+",...>");
			long times = 0L;
			for (LongWritable count : v2s) {
				times += count.get();
				//顯示次數表示輸入的k2,v2的鍵值對數量
				System.out.println("MyReducer輸入鍵值對<"+k2.toString()+","+count.get()+">");
			}
			ctx.write(k2, new LongWritable(times));
		};
	}
	
	
	static class MyCombiner extends Reducer<Text, LongWritable, Text, LongWritable>{
		protected void reduce(Text k2, java.lang.Iterable<LongWritable> v2s, Context ctx) throws java.io.IOException ,InterruptedException {
			//顯示次數表示redcue函數被調用了多少次,表示k2有多少個分組
			System.out.println("Combiner輸入分組<"+k2.toString()+",...>");
			long times = 0L;
			for (LongWritable count : v2s) {
				times += count.get();
				//顯示次數表示輸入的k2,v2的鍵值對數量
				System.out.println("Combiner輸入鍵值對<"+k2.toString()+","+count.get()+">");
			}
			
			ctx.write(k2, new LongWritable(times));
			//顯示次數表示輸出的k2,v2的鍵值對數量
			System.out.println("Combiner輸出鍵值對<"+k2.toString()+","+times+">");
		};
	}
}

問:爲什麼使用Combiner?

答:Combiner發生在Map端,對數據進行規約處理,數據量變小了,傳送到reduce端的數據量變小了,傳輸時間變短,作業的整體時間變短。


問:爲什麼Combiner不作爲MR運行的標配,而是可選步驟哪?

答:因爲不是所有的算法都適合使用Combiner處理,例如求平均數。


問:Combiner本身已經執行了reduce操作,爲什麼在Reducer階段還要執行reduce操作哪?

答:combiner操作發生在map端的,處理一個任務所接收的文件中的數據,不能跨map任務執行;只有reduce可以接收多個map任務處理的數據。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章