一個MapReduce 程式示例 細節決定成敗(八):TotalOrderPartitioner

self_control發表於2016-05-30
在上一個實驗中 一個MapReduce 程式示例 細節決定成敗(七) :自定義Key 及RecordReader,使用自定義的RecordReader 從split 中讀取資訊,封裝到自定義的key中。
這一步在使用TotalOrderPartitioner 進行詞頻統計是非常重要的。
下面進入主題:在一個MapReduce 程式示例 細節決定成敗(五) :Partitioner  中,演示瞭如何使用一下自定義Partitioner。
我們使用了一個自定義的Partitioner,來實現當設定3個reducer時輸出字元的排序問題。為了簡化問題,我使用在partitioner中直接進行判斷。
這裡面其實是有兩個我們應該注意的問題。
1、在partitioner程式碼中寫死了,只有3個reducer,那如果我們想要把reducer的個數設定成其它值,還需要改程式碼。
2、partitioner 的一個作用就是解決資料傾斜的問題,但我們上次寫的那個partitioner不適用
那接下來我們介紹的 TotalOrderPartitioner 就完美解決了上述兩個問題。
本次涉及到的內容有。 
1、生成樣本partition file
2、使用使用CacheFile功能把partition file 分發到各個map task節點。
3、配置使用TotalOrderPartitioner.

因為TotaOrderlPartitioner是已經封裝到hadoop 系統中了,所以我們只要正確配置就可以了。
partition file :描述了每個字元應該進入到哪兒個reducer中的檔案,功能就是上一篇中我們簡化處理的 a~i 分到0, h~q 分到1,r~z 分到2。
partition file 不用我們手動去寫,而且手動去寫也很難平均分佈各個reducer任務的負載。可以使用InputSampler.RandomSampler完成此項工作的。


點選(此處)摺疊或開啟

  1.  /**
  2.   *  freq:每個key 可以被抽樣次數
  3.   *  numSamples:總樣本數  
  4.   */
  5. InputSampler.RandomSampler(double freq, int numSamples);

  6.  /**
  7.   *  freq:每個key 可以被抽樣次數
  8.   *  numSamples:總樣本數
  9.   *  maxSplitsSampled:定義最多從多少個splits 上進行抽樣
  10.   */
  11. InputSampler.RandomSampler(double freq, int numSamples, int maxSplitsSampled);

預設抽樣是在所有的split上進行的,當輸入檔案很大的時候,使用第一個方法進行抽樣代價會很大。
當處理檔案很大的時候,加上第三個引數maxSplitsSampled 限定最多處理多少個split。

freq 引數: 被抽樣到的split中的每一個key 被選中的概率。 numSamples 代表抽樣的總樣本數。 比如從10000條資料中抽取 10條數,假如freq 設定為0.5,那按概率大約執行到20條的時候,樣本數就夠了。
但然後程式會繼續執行,每個被抽中的樣本去隨機替換一條已經抽出的10條記錄中的一條,一直到最後。


下面貼一下配置使用TotaOrderlPartitioner的程式碼 
         關注 getPartitionFile和 new URI 的寫法。

點選(此處)摺疊或開啟

  1.                 job.setPartitionerClass(TotalOrderPartitioner.class);
  2.                 job.setNumReduceTasks(3);
  3.                 RandomSampler<MyKey, NullWritable> sampler
  4.                            = new InputSampler.RandomSampler<MyKey,NullWritable>(0.6, 20, 3);
  5.                 InputSampler.writePartitionFile(job, sampler);

  6.                 String partitionFile = TotalOrderPartitioner.getPartitionFile(job.getConfiguration());
  7.                 URI uri = new URI(partitionFile+"#"+TotalOrderPartitioner.DEFAULT_PATH);
  8.                 job.addCacheFile(uri);


注意: 1、RecordReader的key與map輸入輸出key和reduce的輸入key  要保持一致。  因為取樣是從job 配置的InputFormat中得到RecordReader 然後不斷的得到Key ,從中取樣。 

               取到的樣使用TotalOrderPartitioner 得到的partitionFile 是供分配到不同reducer的,所以RecordReader的key 要與Reduer的輸入key 一致,又 RecordReader的key value 是輸入到 map 中的,

                Reduce 的輸入又是Map的輸出。繞了半天結論就是: RecordReader的key, Map 的輸入輸出key ,Reduce的輸入key 是要保持一致的。

            2、使用TotalOrderPartitioner前一定要配置了reduceTask 的個數、InputFormat、和上面討論的 key class。

貼一下結果:


點選(此處)摺疊或開啟

  1. wordcount.MyKey@212cb585 8
  2. wordcount.MyKey@212cb585 3
  3. wordcount.MyKey@212cb585 4
  4. wordcount.MyKey@212cb585 4
  5. wordcount.MyKey@212cb585 11
  6. wordcount.MyKey@212cb585 4
  7. wordcount.MyKey@212cb585 3
  8. wordcount.MyKey@168497f6 8
  9. wordcount.MyKey@168497f6 5
  10. wordcount.MyKey@168497f6 3
  11. wordcount.MyKey@168497f6 3
  12. wordcount.MyKey@168497f6 6
  13. wordcount.MyKey@168497f6 7
  14. wordcount.MyKey@168497f6 4
  15. wordcount.MyKey@168497f6 12
  16. wordcount.MyKey@168497f6 3
  17. wordcount.MyKey@168497f6 3
  18. wordcount.MyKey@2e67cd84 13
  19. wordcount.MyKey@2e67cd84 4
  20. wordcount.MyKey@2e67cd84 4
  21. wordcount.MyKey@2e67cd84 6
  22. wordcount.MyKey@2e67cd84 7
  23. wordcount.MyKey@2e67cd84 3
  24. wordcount.MyKey@2e67cd84 6
  25. wordcount.MyKey@2e67cd84 3


       注意到key的格式,這是由於我們的MyKey沒有覆寫toString 方法,輸出的時候直接使用了Object的toString實現。
   

點選(此處)摺疊或開啟

  1. @Override
  2. public String toString(){
  3.        return String.valueOf(this.c);
  4. }
結果分析:  下面資料是執行結果,可以看到抽樣劃分的界限是a~e  f~p q~z 。

點選(此處)摺疊或開啟

  1. [train@sandbox src]$ hdfs dfs -cat output/*0
  2. a 8
  3. b 3
  4. c 4
  5. d 4
  6. e 11
  7. [train@sandbox src]$ hdfs dfs -cat output/*1
  8. f 4
  9. g 3
  10. h 8
  11. i 5
  12. j 3
  13. k 3
  14. l 6
  15. m 7
  16. n 4
  17. o 12
  18. p 3
  19. [train@sandbox src]$ hdfs dfs -cat output/*2
  20. q 3
  21. r 13
  22. s 4
  23. t 4
  24. u 6
  25. w 7
  26. x 3
  27. y 6
  28. z 3



下面貼出主程式程式碼

點選(此處)摺疊或開啟

  1. package wordcount;

  2. import java.io.IOException;
  3. import java.net.URI;

  4. import org.apache.hadoop.conf.Configuration;
  5. import org.apache.hadoop.conf.Configured;
  6. import org.apache.hadoop.fs.Path;
  7. import org.apache.hadoop.io.IntWritable;
  8. import org.apache.hadoop.io.NullWritable;
  9. import org.apache.hadoop.io.Text;
  10. import org.apache.hadoop.mapred.lib.TotalOrderPartitioner;
  11. import org.apache.hadoop.mapreduce.Job;
  12. import org.apache.hadoop.mapreduce.Mapper;
  13. import org.apache.hadoop.mapreduce.Reducer;
  14. import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
  15. import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
  16. import org.apache.hadoop.mapreduce.lib.partition.InputSampler;
  17. import org.apache.hadoop.mapreduce.lib.partition.InputSampler.RandomSampler;
  18. import org.apache.hadoop.util.Tool;
  19. import org.apache.hadoop.util.ToolRunner;
  20. import org.apache.log4j.Logger;

  21. public class MyWordCountJob extends Configured implements Tool {
  22.         Logger log = Logger.getLogger(MyWordCountJob.class);

  23.         public static class MyWordCountMapper extends
  24.                         Mapper<MyKey, NullWritable, MyKey, IntWritable> {
  25.                 Text mKey = new Text();
  26.                 IntWritable mValue = new IntWritable(1);
  27.                 @Override
  28.                 protected void map(MyKey key, NullWritable value, Context context)
  29.                                 throws IOException, InterruptedException {
  30.                         context.write(key, mValue);
  31.                 }
  32.         }

  33.         public static class MyWordCountReducer extends Reducer<MyKey, IntWritable, MyKey, IntWritable> {
  34.                 Text rkey = new Text();
  35.                 IntWritable rvalue = new IntWritable(1);
  36.                 @Override
  37.                 protected void reduce(MyKey key, Iterable<IntWritable> values,Context context)
  38.                                 throws IOException, InterruptedException {

  39.                         int n=0;
  40.                         for(IntWritable value :values){
  41.                                 n+= value.get();
  42.                         }
  43.                         rvalue.set(n);
  44.                         context.write(key, rvalue);
  45.                 }
  46.         }

  47.         @Override
  48.         public int run(String[] args) throws Exception {
  49.                 //valid the parameters
  50.                 if(args.length !=2){
  51.                         return -1;
  52.                 }

  53.                 Job job = Job.getInstance(getConf(), "MyWordCountJob");
  54.                 job.setJarByClass(MyWordCountJob.class);

  55.                 Path inPath = new Path(args[0]);
  56.                 Path outPath = new Path(args[1]);

  57.                 outPath.getFileSystem(getConf()).delete(outPath,true);
  58.                 TextInputFormat.setInputPaths(job, inPath);
  59.                 TextOutputFormat.setOutputPath(job, outPath);


  60.                 job.setMapperClass(MyWordCountJob.MyWordCountMapper.class);
  61.                 job.setReducerClass(MyWordCountJob.MyWordCountReducer.class);

  62.                 job.setInputFormatClass(MyCombinedFilesInputFormat.class);
  63.                 MyCombinedFilesInputFormat.setMaxInputSplitSize(job, 1024*1024*64);
  64.                 job.setOutputFormatClass(TextOutputFormat.class);
  65.                 job.setMapOutputKeyClass(MyKey.class);
  66.                 job.setMapOutputValueClass(IntWritable.class);
  67.                 job.setOutputKeyClass(MyKey.class);
  68.                 job.setOutputValueClass(IntWritable.class);

  69.                 job.setPartitionerClass(TotalOrderPartitioner.class);
  70.                 job.setNumReduceTasks(3);
  71.                 RandomSampler<MyKey, NullWritable> sampler
  72.                            = new InputSampler.RandomSampler<MyKey,NullWritable>(0.6, 20, 3);
  73.                 InputSampler.writePartitionFile(job, sampler);

  74.                 String partitionFile = TotalOrderPartitioner.getPartitionFile(job.getConfiguration());
  75.                 URI uri = new URI(partitionFile+"#"+TotalOrderPartitioner.DEFAULT_PATH);
  76.                 job.addCacheFile(uri);

  77.                 return job.waitForCompletion(true)?0:1;
  78.         }
  79.         public static void main(String [] args){
  80.                 int result = 0;
  81.                 try {
  82.                         result = ToolRunner.run(new Configuration(), new MyWordCountJob(), args);
  83.                 } catch (Exception e) {
  84.                         e.printStackTrace();
  85.                 }
  86.                 System.exit(result);
  87.         }

  88. }



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30066956/viewspace-2109331/,如需轉載,請註明出處,否則將追究法律責任。

相關文章