Hadoop實踐(四)---MR作業配置

1、向作業傳遞定製的參數

Hadoop自身使用一個配置對象類存儲所有作業的配置屬性,也可以使用這個對象將參數傳遞到Mapper和Reducer

  1. MR的driver類通過屬性來配置JobConf對象,這些屬性包括輸入格式,輸出格式,Mapper類等。如果要引入自定義的屬性,需要在這個配置對象中給屬性一個唯一的名稱並設置它的值。
  2. 這個配置對象會被傳遞到所有的TaskTracker(AM),然後作業中的所有任務就能夠看到配置對象中的屬性
  3. Mapper和Reducer就可以通過讀取該配置對象並獲得它的屬性值

Configuration類(JonConf的父類)有許多通用的setter方法,屬性採用鍵值對的形式,鍵必須是string,而值可以是常用類型的任意一個。

常用的setter方法:

  public void set(String name,String value)
  public void setBoolean(String name, boolean value)
  public void setInt(String name, int value)
  public void setLong(String name, long value)
  public void setStrings(String name, String... values)

常用的getter方法:

 public String get(String name)
 public String get(String name, String defaultValue)
 public boolean getBoolean(String name, boolean defaultValue)
 public float getFloat(String name, float defaultValue)
 public int getInt(String name, int defaultValue)
 public long getLong(String name, long defaultValue)
 public String getBoolean(String name, String... defaultValue)

Tips:在Hadoop內部,所有的屬性都存爲字符串

  1. driver類首選設置配置對象的屬性,讓它們在所有的任務中可見
  2. Mapper和Reducer可以訪問Configure()方法中的配置對象
  3. 任務初始化時會調用configure(),它已經被重寫爲可以提取和存儲設置的屬性
  4. map和reduce方法訪問這些屬性的副本
package MR;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;

import java.io.IOException;
import java.util.Iterator;

/**
 * Created by Promacanthus on 2017/6/29.
 */
public class tample {
    public int run(String[] args)throws Exception{
        Configuration conf = new Configuration();
        JobConf job = new JobConf(conf);
        job.setInt("myjob.property",Integer.parseInt(args[2]));
        JobClient.runJob(job);
        return 0;
    }
    public static class MapClass extends MapReduceBase implements Mapper<Text,Text,Text,Text>{
        int myproperty;
        public void configure(JobConf job){
            myproperty = job.getInt("myjob.property",0);
        }
        public void map(Text key, Text value, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {

        }
    }
    public static class Reduc extends MapReduceBase implements Reducer<Text,Text,Text,Text>{
        int myproperty;
        public void configure(JobConf job){
            myproperty = job.getInt("myjob.property",0);
        }
        public void reduce(Text key, Iterator<Text> values, OutputCollector<Text, Text> output, Reporter reporter) throws IOException {

        }
    }
}

當允許用戶設定特定屬性時,在driver中最好針對用戶的輸入進行驗證

2、查看任務特定信息

除了獲取自定義屬性和全局配置外,可以使用配置對象上的getter方法獲得當前任務和作業狀態的一些信息

例如,通過map.input.file屬性來得到當前mao任務的文件路徑

在配置對象中可獲得的任務特定狀態信息:

屬性 類型 描述
maperd.job.id String 作業ID
mapred.jar String 作業目錄中jar的位置
job.local.dir String 作業的本地空間
mapred.tip.id String 任務ID
mapred.task.id String 任務重試ID
mapred.task.is.map boolean 標誌量,表示是否爲一個map任務
mapred.task.partition int 作業內部的任務ID
map.input.file String Mapper讀取的文件路徑
map.input.start long 當前Mapper輸入分片的文件偏移量
map.input.length long 當前Mapper輸入分片中的字節數
mapred.work.output.dir String 任務的工作(即臨時)輸出目錄

3、劃分爲多個輸出文件

默認情況下,所有的MR作業都輸出一組文件。然而在有些場景下,輸出多組文件或把一個數據集分爲多個數據集更爲方便

3-1、MultipleOutputFormat

MultipleOutputFormat提供了一個簡單的方法,將相似的記錄結組爲不同的數據集,在寫每條輸出記錄之前,這個outputFormat類調用一個內部方法來確定要寫入的文件名

更具體的說,擴展MultipleOutputFormat的某個子類,並實現generateFileNameKeyValue()方法,擴展的子類將決定輸出的格式。

輸出格式
MultipleTextOutputFormat 輸出文本文件
MultipleSequenceFileOutputFormat 輸出序列文件

不論擴展了哪一個子類,都需要重寫下面的方法以返回每個輸出鍵值對的文件名:

  protected String generateFileNameForKeyValue(K key,V value, String name)

默認實現返回參數name,即文件名

示例代碼如下:

package HDFS;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.mapred.lib.MultipleTextOutputFormat;
import org.apache.hadoop.util.Tool;

import java.io.IOException;

/**
 * Created by Promacanthus on 2017/6/29.
 */
public class MultiFile extends Configured implements Tool {
    public static class MapClass extends MapReduceBase implements Mapper<LongWritable, Text, NullWritable, Text> {

        public void map(LongWritable key, Text value, OutputCollector<NullWritable, Text> output, Reporter reporter) throws IOException {
            output.collect(NullWritable.get(), value);
        }
    }

    public static class PartitionByCountryMTOF extends MultipleTextOutputFormat<NullWritable, Text> {
        protected String generateFielNameForKeyValue(NullWritable key, Text value, String filename) {
            String[] arr = value.toString().split(",", -1);
            String country = arr[4].substring(1, 3);
            return country + "/" + filename;
        }
    }

    public int run(String[] args) throws Exception {
        Configuration conf = getConf();
        JobConf job = new JobConf();

        Path in = new Path(args[0]);
        Path out = new Path(args[1]);
        FileInputFormat.setInputPathFilter(job,in);
        FileOutputFormat.setOutputPath(job,out);

        job.setJobName("MultiFile");
        job.setMapperClass(MapClass.class);

        job.setInputFormat(TextInputFormat.class);
        job.setOutputFormat(PartitionByCountryMTOF.class);
        job.setOutputKeyClass(NullWritable.class);
        job.setOutputValueClass(Text.class);

        job.setNumReduceTasks(0);

        JobClient.runJob(job);

        return 0;
    }
}

    public static void main(String[] args) throws Exception {
        int res = ToolRunner.run(new Configuration(),new MultiFile(),args);
        System.exit(res);
    }

在輸入出目錄中可以看到每個國家都有一個單獨的目錄

3-2、MultipleOutputs

MultipleOutputs不要求給每條記錄請求文件名,而是創建多個OutputCollector,每個OutputCollector可以有自己的OutputFormat和鍵值對類型,MR程序決定如何向每個OutputCollector輸出數據

示例代碼如下:

package HDFS;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.mapred.lib.MultipleOutputs;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;

/**
 * Created by Promacanthus on 2017/6/29.
 */
public class NewMultipleFile extends Configured implements Tool {
    public static class MapClass extends MapReduceBase implements Mapper<LongWritable, Text, NullWritable, Text> {
        private MultipleOutputs mos;
        private OutputCollector<NullWritable, Text> collector;

        public void configure(JobConf conf) {
            mos = new MultipleOutputs(conf);
        }

        public void map(LongWritable key, Text value, OutputCollector<NullWritable, Text> output, Reporter reporter) throws IOException {
            String[] arr = value.toString().split(",", -1);
            String chrono = arr[0] + "," + arr[1] + "." + arr[2];
            String geo = arr[0] + "," + arr[4] + "." + arr[5];

            collector = mos.getCollector("chrono", reporter);
            collector.collect(NullWritable.get(), new Text(chrono));
            collector = mos.getCollector("geo", reporter);
            collector.collect(NullWritable.get(), new Text());
        }

        public void close() throws IOException {
            mos.close();
        }
    }

    public int run(String[] args) throws Exception {
        Configuration conf = getConf();
        JobConf job = new JobConf(conf,MultiFile.class);

        Path in = new Path(args[0]);
        Path out = new Path(args[1]);
        FileInputFormat.setInputPaths(job,in);
        FileOutputFormat.setOutputPath(job,out);

        job.setJobName("MultiFile");
        job.setMapperClass(MultiFile.MapClass.class);

        job.setInputFormat(TextInputFormat.class);
        job.setOutputFormat(MultiFile.PartitionByCountryMTOF.class);
        job.setOutputKeyClass(NullWritable.class);
        job.setOutputValueClass(Text.class);

        job.setNumReduceTasks(0);

        MultipleOutputs.addNamedOutput(job,"chrono",TextOutputFormat.class,NullWritable.class,Text.class);
        MultipleOutputs.addNamedOutput(job,"gep",TextOutputFormat.class,NullWritable.class,Text.class);

        JobClient.runJob(job);

        return 0;
    }

    public static void main(String[] args) throws Exception {
        int res = ToolRunner.run(new Configuration(),new MultiFile(),args);
        System.exit(res);
    }
}

若要使用MultipleOutputs,MR程序的driver類必須設置它想要使用的輸出收集器

在輸出目錄中有一組以chrono爲前綴的文件,另一組以geo爲前綴的文件

4、以數據庫作爲輸入輸出

  • 建立一個MR程序通過直接查詢數據庫來去的輸入數據,而不是從HDFS中讀取文件,但這樣性能不好
  • 或者將數據集從數據庫複製到HDFS,使用標準的數據庫管理工具dump或者HDFS的shell命令put

DBOutputFormat是訪問數據庫的關鍵類,在driver類中將輸入輸出格式設置爲這個類,指定配置,便能夠連接到該數據庫了。具體配置通過DBConfiguration中的靜態方法configureDB():

 public static void configureDB(JobConf job, String driverClass, String dbUrl, String userName, String passwd)

然後指定將要寫入的表,以及哪些字段,使用DBOutputFormat中的今天方法setOutput():

 public static void setOutput(JobConf job, String tableName, String... fieldNames)

在driver類中添加如下代碼:

 job.setOutputFormat(DBOutputFormat.class);
        DBConfiguration.configureDB(job,"com.mysql.jdbc.Driver","jdbc:mysql://db.host.com/mydb","username","password");

使用DBOutputFormat將強制輸出的鍵實現DBWritable接口

Tips:從Hadoop內部讀寫數據庫僅適用於憑藉Hadoop標準來說相對較小的數據集,除非數據庫和Hadoop並行,否則數據庫將成爲性能瓶頸

5、保持輸出的順序

MR框架保證每個reducer的輸入都按鍵來排序,在許多情況下,reducer只是對鍵值對中值的部分做簡單的計算,輸出仍然保持順序排序。MR框架並不能保證reducer輸出的順序,它只是已經排序好的輸入以及reducer鎖執行的操作類型的副產品

如何讓reducer的輸出時有序的呢,即part-r-00000中所有記錄小於part-r-00001,以此類推?關鍵在於Partitioner操作

  1. Partitioner的任務是確定地爲每個鍵分配一個reducer。相同鍵的所有記錄都結成組並在reducer階段被集中處理
  2. Partitioner的一個重要設計需求是在reducer之間達到負載平衡(沒有一個reducer會比其他reducer被分配到更多的鍵)
  3. 由於沒有以前對鍵的分配信息,Partitioner默認使用散列函數來均勻的分配,分配完全隨機,不存在任何順序
  4. 如果事先知道鍵是大致均勻分佈的,可以使用Partitioner給每個reducer分配一個鍵的範圍

TotalOrderPartitioner是一個可以保證在輸入分區之間,而不僅僅是分區內部排序的Partitioner,這個類利用一個排好序的分區鍵組讀取一個序列文件,並進一步將不同區域的鍵分配到reducer上

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