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

self_control發表於2016-06-01
在mr 框架中,用的最普遍的的一個功能就是比較大小。 map輸入時按key排序要比較。reduce的shuff /sort 階段要比較。
所以我們自定義的key型別都是要實現WritableComparator 介面。這個介面中有三個方法需要我們實現。
先看一個簡單的自定義key型別 的程式碼。我們關注其中有一個compareTo 方法。

點選(此處)摺疊或開啟

  1. package wordcount;

  2. import java.io.DataInput;
  3. import java.io.DataOutput;
  4. import java.io.IOException;

  5. import org.apache.hadoop.io.WritableComparable;

  6. public class MyKey implements WritableComparable<MyKey> {

  7.         private char c;
  8.         @Override
  9.         public void write(DataOutput out) throws IOException {
  10.                 out.writeChar(c);
  11.         }

  12.         @Override
  13.         public void readFields(DataInput in) throws IOException {
  14.                 c= in.readChar();
  15.         }

  16.         @Override
  17.         public int compareTo(MyKey key) {
  18.                 if(c==key.c)
  19.                         return 0;
  20.                 else if(c> key.c)
  21.                         return 1;
  22.                 else
  23.                         return -1;
  24.         }

  25.         public char getC() {
  26.                 return c;
  27.         }

  28.         public void setC(char c) {
  29.                 this.c = c;
  30.         }
  31.         @Override
  32.         public String toString(){
  33.                 return String.valueOf(this.c);
  34.         }

  35. }
注意觀察我們 public int compareTo(MyKey key) ; 引數是MyKey型別。 就是是說在呼叫這個比較的時候要做一個反序列化操作成Mykey 物件再比較的過程。 那在大量資料集的情況下,如何減少比較的開銷呢。
能不能不反序列化,直接使用序列化成的byte進行比較呢? 答案是肯定的。我們這次介紹的RawComparator 就是用來做這個的。
在真正寫我們自己的程式碼之前 ,先介紹一個小技巧:那就是寫任何hadoop MapReduce可程式設計元件時,先參考系統自帶的預設實現程式碼。
根據這個習慣,我們找一個系統自帶的型別看看原始碼。那就找IntWritable吧。

點選(此處)摺疊或開啟

  1. /** A WritableComparable for ints. */
  2. @InterfaceAudience.Public
  3. @InterfaceStability.Stable
  4. public class IntWritable implements WritableComparable<IntWritable> {
  5.   private int value;

  6.   public IntWritable() {}

  7.   public IntWritable(int value) { set(value); }

  8.   /** Set the value of this IntWritable. */
  9.   public void set(int value) { this.value = value; }

  10.   /** Return the value of this IntWritable. */
  11.   public int get() { return value; }

  12.   @Override
  13.   public void readFields(DataInput in) throws IOException {
  14.     value = in.readInt();
  15.   }

  16.   @Override
  17.   public void write(DataOutput out) throws IOException {
  18.     out.writeInt(value);
  19.   }

  20.   /** Returns true iff <code>o</code> is a IntWritable with the same value. */
  21.   @Override
  22.   public boolean equals(Object o) {
  23.     if (!(o instanceof IntWritable))
  24.       return false;
  25.     IntWritable other = (IntWritable)o;
  26.     return this.value == other.value;
  27.   }

  28.   @Override
  29.   public int hashCode() {
  30.     return value;
  31.   }

  32.   /** Compares two IntWritables. */
  33.   @Override
  34.   public int compareTo(IntWritable o) {
  35.     int thisValue = this.value;
  36.     int thatValue = o.value;
  37.     return (thisValue<thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
  38.   }

  39.   @Override
  40.   public String toString() {
  41.     return Integer.toString(value);
  42.   }

  43.   /** A Comparator optimized for IntWritable. */
  44.   public static class Comparator extends WritableComparator {
  45.     public Comparator() {
  46.       super(IntWritable.class);
  47.     }
  48.     
  49.     @Override
  50.     public int compare(byte[] b1, int s1, int l1,
  51.                        byte[] b2, int s2, int l2) {
  52.       int thisValue = readInt(b1, s1);
  53.       int thatValue = readInt(b2, s2);
  54.       return (thisValue<thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
  55.     }
  56.   }

  57.   static { // register this comparator
  58.     WritableComparator.define(IntWritable.class, new Comparator());
  59.   }
  60. }
IntWritable 的例子中,我們得到:
1、把
WritableComparator 當IntWritable的內部類,寫到裡面,放到一起比較容易找。
2、如何註冊一個自定義的WritableComparator。
  此處注意一下,關注static { WritableComparator.define(IntWritable.class,new Comparator())} 的位置。
  為什麼是寫到IntWritable中,寫到Comparator類中可以不? 為什麼? (不能寫到Comparator中)
那按這個方式,我們寫一下MyKey的RawComparator

點選(此處)摺疊或開啟

  1. package wordcount;

  2. import java.io.DataInput;
  3. import java.io.DataOutput;
  4. import java.io.IOException;

  5. import org.apache.hadoop.io.WritableComparable;
  6. import org.apache.hadoop.io.WritableComparator;
  7. import org.apache.log4j.Logger;

  8. public class MyKey implements WritableComparable<MyKey> {

  9.         private char c;
  10.         @Override
  11.         public void write(DataOutput out) throws IOException {
  12.                 out.writeChar(c);
  13.         }

  14.         @Override
  15.         public void readFields(DataInput in) throws IOException {
  16.                 c= in.readChar();
  17.         }

  18.         @Override
  19.         public int compareTo(MyKey key) {
  20.                 if(c==key.c)
  21.                         return 0;
  22.                 else if(c> key.c)
  23.                         return 1;
  24.                 else
  25.                         return -1;
  26.         }

  27.         public char getC() {
  28.                 return c;
  29.         }

  30.         public void setC(char c) {
  31.                 this.c = c;
  32.         }
  33.         @Override
  34.         public String toString(){
  35.                 return String.valueOf(this.c);
  36.         }

  37.         public static class MyKeyRawComparator extends WritableComparator{

  38.                 Logger log = Logger.getLogger(MyKeyRawComparator.class);
  39.                 public MyKeyRawComparator(){
  40.                         super(MyKey.class);
  41.                 }

  42.                 @Override
  43.                 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
  44.                         log.info("==============MyKeyRawComparator Used");

  45.                         return super.readUnsignedShort(b1,s1) - super.readUnsignedShort(b2, s2);
  46.                 }

  47.         }
  48.         static{
  49.                 WritableComparator.define(MyKey.class,new MyKeyRawComparator());
  50.         }

  51. }
為了驗證我們的RawComparator執行了,在compare方法中加了日誌輸出。同時也可以驗證在什麼時候呼叫了兩個key的比較。
我們理解,應該在map 與reduce階段都會使用到key的比較。下面我們就分別看一下map與reduce的日誌輸出。
開啟瀏覽器輸入 http://xxx.xxx.xxx.xxx:19888/jobhistory 點選對應的job.
mapper的日誌 

reducer的日誌



通過日誌,驗證了我們的預期。

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

相關文章