hadoop,用戶制定類


mapreduce是一個很精巧的構思和設計,對於很多計算問題,程序員通常可以使用默認設置取處理諸多底層的細節,但是,這並不意味着在解決復雜問題時,程序員就可以完全使用這二個函數就可以搞定一切,它還需要更加復雜和靈活的處理機制以及高級的編程技術和方法。本節介紹hadoop中mapreduce比較高級的方法---用戶制定類。為什么要用戶自定義類,一種直觀的猜測就是基本的mapreduce處理不了或者處理的效果不好,才有可能用到用戶制定,隨着深入的學習,這些問題到處可見。比如文檔的倒排索引,制定(優化)的目的就是減少中間鍵,從而減少每次的讀寫I/O和網絡的壓力。

1.  用戶自定義數組類型

    雖然hadoop中內置8種數據類型,我見過8種,但不一定就8種,這里就認為是8種吧,他們都實現了WritableComparable接口,這種好處就是可以被序列化進行網路傳輸和文件存儲。BooleanWritable, ByteWritable, FloatWritable, IntWritable,LongWritable,Text, NullWritble,前幾種大家都可以猜到表示是什么數據類型,Text表示使用UTF格式的存儲的文本。好像visual stdio 中text控件,也是對String類型的一種包裝。

    自定義類型必須符合二個條件,第一:實現Writable接口。第二:如果該函數需要作為主鍵Key使用,或者要比較數值大小時,則要實現WritableComparable接口。下例子是一個圓為例。circle

 1 public class Cricle implements Writable<Circle>{
 2     private float radius,x,y;
 3     public float GetRadius(){return radius;}
 4     public float GetX(){return x;}
 5     public float GetY(){return y;}
 6     public void readFields(DataInput in)throws IOException{
 7         radius=in.readFloat();
 8         x=in.readFloat();
 9         y=in.readFloat();
10     }
11     public void write(DataOutput out)throws IOException{
12         out.writeFloat(radius);
13         out.writeFloat(x);
14         out.writeFloat(y);
15     }
16     public int CompareTo(Circle cl){
17         if(cl.radius==this.radius)
18             return 0;
19         if(cl.x>this.x)
20             return 1;
21         if(cl.y<this.y)
22             return -1;
23     }
24 }

以上代碼就是手寫的,沒有進行測試,肯定還有錯誤,只是向說明問題。readFields() 和write()實現Writable接口中定義的二中方法。

 

2. 用戶制定輸入/輸出

盡管Hadoop提供了較為豐富的數據輸入/輸出格式,可以滿足很多應用的需要,但是,對於特殊的要求,還是要用戶自己制定。比如說,Hadoop默認的數據輸入格式為TextInputFormat,可以將文本文件分塊並逐行讀入以便Map節點進行處理,每行所產生的Key是文本的字節位置的偏移量,而value就是該行的內日內個。但現在需要文件名@文本偏移量作為key,所以就需要制定輸入類,假定現在制定一個數據輸入格式FileNameInputFormat和fileNameRecordReader,比便直接產生FileName@lineoffset.

public class FileNameInputFormat extends FileInputForamt<Text,Text>{
    public RecordReader<Text,Text>createRecordReader(InputSplit split,TaskAttemptContext context)throws IOException{
        FileNameRecordReader fnrr = new FileNameRecordReader();
        fnrr.initialize(split,context);
    }
}
public class FileNameRecordReader extends RecordReader<Text, Text> { String FileName; LineRecordReader lrr = new LineRecordReader(); public Text getCurrentKey() throws IOException { return new Text("(" + FileName + "@" + lrr.getCurrentKey() + ")"); } public Text getCurrentValue() throws IOException, InterruptedException { return new Text(lrr.getCurrentKey().toString()); } public void initialize(InputSplit arg0, TaskAttemptContext arg1) throws IOException { lrr.initialize(arg0, arg1); FileName = ((FileSplit) arg0).getPath().getName(); } }

 沒事什么好講的,仔細看都能看的懂,輸出就不講了,和輸入差不多。

 

3. 用戶制定Patritioner和Combiner

   Patritioner來完成中間結果向Reduce節點分區處理,通常是利用hash進行分配,hash划分主要是靠主鍵進行划分,即相同的主鍵划分到相同桶中,在mapreduce中就是划分到相同的reduce中。那么現在設想一個問題,我需要將這樣的數據划分到一個reduce中,該怎么辦? 數據為 hello:1, hello:2,hello:3,hello:4,......., 顯然,用hash直接划分是不行的,這是我們可以觀察到這些數據都具有一個hello,我們只需要在提取hello作為主鍵,在用hash划分,就可以把這些數據都划分到同一個reduce中,下面就個例子,給出Patritioner代碼:

public class NParterioner extends HashPartitioner<Text,Text>
{
    public getPartition(Text key,Text value, int numReduceTasks){
        
        String t=key.toString().split(":")[0];
        Text term;
        term.set(t);
        super.getPartition(term,value,numReduceTasks);
    }
}

還有像map,reduce, combiner的制定都是一樣的道理。

 

總結:

      用戶類的制定主要取決於特定的應用的場合,但其目標都是不變的(減少中間結果來減小I/O和網絡傳輸數據的壓力)。


作者:BIGBIGBOAT/Liqizhou


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM