Hadoop 使用Combiner提高Map/Reduce程序效率

众所周知,Hadoop框架使用Mapper将数据处理成一个<key,value>键值对,再网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出。


在上述过程中,我们看到至少两个性能瓶颈:

  1. 如果我们有10亿个数据,Mapper会生成10亿个键值对在网络间进行传输,但如果我们只是对数据求最大值,那么很明显的Mapper只需要输出它所知道的最大值即可。这样做不仅可以减轻网络压力,同样也可以大幅度提高程序效率。
  2. 使用专利中的国家一项来阐述数据倾斜这个定义。这样的数据远远不是一致性的或者说平衡分布的,由于大多数专利的国家都属于美国,这样不仅Mapper中的键值对、中间阶段(shuffle)的键值对等,大多数的键值对最终会聚集于一个单一的Reducer之上,压倒这个Reducer,从而大大降低程序的性能。

Hadoop通过使用一个介于Mapper和Reducer之间的Combiner步骤来解决上述瓶颈。你可以将Combiner视为Reducer的一个帮手,它主要是为了削减Mapper的输出从而减少网络带宽和Reducer之上的负载。如果我们定义一个Combiner,MapReducer框架会对中间数据多次地使用它进行处理。


如果Reducer只运行简单的分布式方法,例如最大值、最小值、或者计数,那么我们可以让Reducer自己作为Combiner。但许多有用的方法不是分布式的。以下我们使用求平均值作为例子进行讲解:


Mapper输出它所处理的键值对,为了使单个DataNode计算平均值Reducer会对它收到的<key,value>键值对进行排序,求和。


由于Reducer将它所收到的<key,value>键值的数目视为输入数据中的<key,value>键值对的数目,此时使用Combiner的主要障碍就是计数操作。我们可以重写MapReduce程序来明确的跟踪计数过程。

代码如下:

[java] view plain copy
  1. packagecom;
  2. importjava.io.IOException;
  3. importorg.apache.hadoop.conf.Configuration;
  4. importorg.apache.hadoop.conf.Configured;
  5. importorg.apache.hadoop.fs.Path;
  6. importorg.apache.hadoop.io.DoubleWritable;
  7. importorg.apache.hadoop.io.LongWritable;
  8. importorg.apache.hadoop.io.Text;
  9. importorg.apache.hadoop.mapreduce.Job;
  10. importorg.apache.hadoop.mapreduce.Mapper;
  11. importorg.apache.hadoop.mapreduce.Reducer;
  12. importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;
  13. importorg.apache.hadoop.mapreduce.lib.input.TextInputFormat;
  14. importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
  15. importorg.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
  16. importorg.apache.hadoop.util.Tool;
  17. importorg.apache.hadoop.util.ToolRunner;
  18. publicclassAveragingWithCombinerextendsConfiguredimplementsTool{
  19. publicstaticclassMapClassextendsMapper<LongWritable,Text,Text,Text>{
  20. staticenumClaimsCounters{MISSING,QUOTED};
  21. //MapMethod
  22. publicvoidmap(LongWritablekey,Textvalue,Contextcontext)throwsIOException,InterruptedException{
  23. Stringfields[]=value.toString().split(",",-20);
  24. Stringcountry=fields[4];
  25. StringnumClaims=fields[8];
  26. if(numClaims.length()>0&&!numClaims.startsWith("\"")){
  27. context.write(newText(country),newText(numClaims+",1"));
  28. }
  29. }
  30. }
  31. publicstaticclassReduceextendsReducer<Text,Text,Text,DoubleWritable>{
  32. //ReduceMethod
  33. publicvoidreduce(Textkey,Iterable<Text>values,Contextcontext)throwsIOException,InterruptedException{
  34. doublesum=0;
  35. intcount=0;
  36. for(Textvalue:values){
  37. Stringfields[]=value.toString().split(",");
  38. sum+=Double.parseDouble(fields[0]);
  39. count+=Integer.parseInt(fields[1]);
  40. }
  41. context.write(key,newDoubleWritable(sum/count));
  42. }
  43. }
  44. publicstaticclassCombineextendsReducer<Text,Text,Text,Text>{
  45. //ReduceMethod
  46. publicvoidreduce(Textkey,Iterable<Text>values,Contextcontext)throwsIOException,InterruptedException{
  47. doublesum=0;
  48. intcount=0;
  49. for(Textvalue:values){
  50. Stringfields[]=value.toString().split(",");
  51. sum+=Double.parseDouble(fields[0]);
  52. count+=Integer.parseInt(fields[1]);
  53. }
  54. context.write(key,newText(sum+","+count));
  55. }
  56. }
  57. //runMethod
  58. publicintrun(String[]args)throwsException{
  59. //CreateandRuntheJob
  60. Jobjob=newJob();
  61. job.setJarByClass(AveragingWithCombiner.class);
  62. FileInputFormat.addInputPath(job,newPath(args[0]));
  63. FileOutputFormat.setOutputPath(job,newPath(args[1]));
  64. job.setJobName("AveragingWithCombiner");
  65. job.setMapperClass(MapClass.class);
  66. job.setCombinerClass(Combine.class);
  67. job.setReducerClass(Reduce.class);
  68. job.setInputFormatClass(TextInputFormat.class);
  69. job.setOutputFormatClass(TextOutputFormat.class);
  70. job.setOutputKeyClass(Text.class);
  71. job.setOutputValueClass(Text.class);
  72. System.exit(job.waitForCompletion(true)?0:1);
  73. return0;
  74. }
  75. publicstaticvoidmain(String[]args)throwsException{
  76. intres=ToolRunner.run(newConfiguration(),newAveragingWithCombiner(),args);
  77. System.exit(res);
  78. }
  79. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值