Hadoop 實例10 Join講解3: 將人員的地址ID完善成爲地址名稱,輸出格式要求:人員Id,姓名,地址 ----優化方案

1、原始數據
人員ID 人員名稱 地址ID

    1 張三 1
    2 李四 2
    3 王五 1
    4 趙六 3
    5 馬七 3

另外一組爲地址信息:
地址ID 地址名稱

    1 北京
    2 上海
    3 廣州

2、處理說明
該處理接着上一講,我們對這個實現進行了總結,最主要的問題就是實現的可擴展性,由於在reduce端我們通過一個List數據結構保存了所有的某個外鍵的對應的所有人員信息,
而List的最大值爲Integer.MAX_VALUE,所以 在數據量巨大的時候,會造成List越界的錯誤.所以對這個實現的優化顯得很有必要.
3、優化說明
結合第一種實現方式,我們看到第一種方式最需要改進的地方就是如果對於某個地址ID的迭代器values,如果values的第一個元素是地址信息的話,
那麼,我們就不需要緩存所有的人員信息了.如果第一個元素是地址信息,我們讀出地址信息後,後來就全部是人員信息,那麼就可以將人員的地址置爲相應的地址.
現在我們回頭看看mapreduce的partition和shuffle的過程,partitioner的主要功能是根據reduce的數量將map輸出 的結果進行分塊,將數據送入到相應的reducer,
所有的partitioner都必須實現Partitioner接口並實現getPartition 方法,該方法的返回值爲int類型,並且取值範圍在0-numOfReducer-1,
從而能夠將map的輸出輸入到相應的reducer中,對於某個 mapreduce過程,Hadoop框架定義了默認的partitioner爲HashPartition,
該Partitioner使用key的 hashCode來決定將該key輸送到哪個reducer;shuffle將每個partitioner輸出的結果根據key進行group以及排序,
將具有相同key的value構成一個valeus的迭代器,並根據key進行排序分別調用開發者定義的reduce方法進行歸併.
從shuffle的過 程我們可以看出key之間需要進行比較,通過比較才能知道某兩個key是否相等或者進行排序,
因此mapduce的所有的key必須實現 comparable接口的compareto()方法從而實現兩個key對象之間的比較.
回到我們的問題,我們想要的是將地址信息在排序的過程中排到最前面,前面我們只通過locId進行比較的方法就不夠用了,
因爲其無法標識出是地址表中的數據 還是人員表中的數據.因此,我們需要實現自己定義的Key數據結構,完成在想共同locId的情況下地址表更小的需求.
由於map的中間結果需要寫到磁盤 上,因此必須實現writable接口.具體實現如下:

4、構造用於排序的key

package cn.edu.bjut.jointwo;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.io.WritableComparable;

public class UserKey implements WritableComparable<UserKey> {

    private int keyId;
    private boolean isPrimary;

    public void write(DataOutput out) throws IOException {
        out.writeInt(keyId);
        out.writeBoolean(isPrimary);

    }

    public void readFields(DataInput in) throws IOException {
        this.keyId = in.readInt();
        this.isPrimary = in.readBoolean();
    }

    public int compareTo(UserKey o) {
        if(this.keyId == o.getKeyId()) {
            if(this.isPrimary == o.isPrimary()) {
                return 0;
            } else {
                return this.isPrimary ? 1 : -1;
            }
        } else {
            return this.keyId > o.getKeyId() ? 1 : -1;
        }
    }

    @Override
    public int hashCode() { //partition 使用key的hashCode方法決定該記錄發往那個一reduce numOfReduce-1
        return this.getKeyId();
    }

    public int getKeyId() {
        return keyId;
    }

    public void setKeyId(int keyId) {
        this.keyId = keyId;
    }

    public boolean isPrimary() {
        return isPrimary;
    }

    public void setPrimary(boolean isPrimary) {
        this.isPrimary = isPrimary;
    }

}

5、構造用於group的比較器
有 了這個數據結構,我們又發現了一個新的問題——就是shuffle的group過程,shuffle的group過程默認使用的是key的 compareTo()方法.
剛纔我們添加的自定義Key沒有辦法將具有相同的locId的地址和人員放到同一個group中(因爲從compareTo 方法中可以看出他們是不相等的).
不過hadoop框架提供了OutputValueGoupingComparator可以讓使用者自定義key的 group信息.
我們需要的就是自己定義個groupingComparator就可以啦!看看這個比較器吧!

package cn.edu.bjut.jointwo;

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

public class MyComparator extends WritableComparator {

    protected MyComparator() {
        super(UserKey.class, true);
    }

    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        UserKey a1 = (UserKey) a;
        UserKey a2 = (UserKey) b;
        if(a1.getKeyId() == a2.getKeyId()) {
            return 0;
        } else {
            return a1.getKeyId() > a2.getKeyId() ? 1 : -1;
        }
    }
}

6Map程序:

package cn.edu.bjut.jointwo;

import java.io.IOException;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

public class JoinMapper extends Mapper<LongWritable, Text, UserKey, Member> {

    @Override
    protected void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {
        String line = value.toString();
        String[] arr = line.split("\t");
        if(arr.length >= 3) {
            Member m = new Member();
            m.setUserNo(arr[0]);
            m.setUserName(arr[1]);
            m.setCityNo(arr[2]);

            UserKey userKey = new UserKey();
            userKey.setKeyId(Integer.parseInt(arr[2]));
            userKey.setPrimary(true);
            context.write(userKey, m);
        } else {
            Member m = new Member();
            m.setCityNo(arr[0]);
            m.setCityName(arr[1]);

            UserKey userKey = new UserKey();
            userKey.setKeyId(Integer.parseInt(arr[0]));
            userKey.setPrimary(false);
            context.write(userKey, m);
        }
    }

}

7.Reduce程序:

package cn.edu.bjut.jointwo;

import java.io.IOException;

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

public class JoinReducer extends Reducer<UserKey, Member, Text, NullWritable> {

    @Override
    protected void reduce(UserKey key, Iterable<Member> values, Context context)
            throws IOException, InterruptedException {
        Member m = null;
        int num = 0;
        for(Member member : values) {
            if(0 == num) {
                m = new Member(member);
            } else {
                Member tmp = new Member(member);
                tmp.setCityName(m.getCityName());
                context.write(new Text(tmp.toString()), NullWritable.get());
            }
            num++;
        }
    }

}

8.主程序:

package cn.edu.bjut.jointwo;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class MainJob {

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = new Job(conf, "jointwo");
        job.setJarByClass(MainJob.class);

        job.setMapperClass(JoinMapper.class);
        job.setMapOutputKeyClass(UserKey.class);
        job.setMapOutputValueClass(Member.class);

        job.setReducerClass(JoinReducer.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);
        job.setGroupingComparatorClass(MyComparator.class);

        FileInputFormat.addInputPath(job, new Path(args[0]));

        Path outPathDir = new Path(args[1]);
        FileSystem fs = FileSystem.get(conf);
        if(fs.exists(outPathDir)) {
            fs.delete(outPathDir, true);
        }

        FileOutputFormat.setOutputPath(job, outPathDir);

        job.waitForCompletion(true);
    }

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