MapReduce InputFormat——DBInputFormat

Thinkgamer_gyt發表於2015-11-30

一、背景

     為了方便MapReduce直接訪問關係型資料庫(Mysql,Oracle),Hadoop提供了DBInputFormat和DBOutputFormat兩個類。通過

DBInputFormat類把資料庫表資料讀入到HDFS,根據DBOutputFormat類把MapReduce產生的結果集匯入到資料庫表中。

二、技術細節

1、DBInputFormat(Mysql為例),先建立表:

CREATE TABLE studentinfo (
  id INTEGER NOT NULL PRIMARY KEY,
  name VARCHAR(32) NOT NULL);
2、由於0.20版本對DBInputFormat和DBOutputFormat支援不是很好,該例用了0.19版本來說明這兩個類的用法。
3、DBInputFormat用法如下:
  1. public class DBInput {  
  2.    // DROP TABLE IF EXISTS `hadoop`.`studentinfo`;  
  3.    // CREATE TABLE studentinfo (  
  4.    // id INTEGER NOT NULL PRIMARY KEY,  
  5.    // name VARCHAR(32) NOT NULL);  
  6.   
  7.    public static class StudentinfoRecord implements Writable, DBWritable {  
  8.      int id;  
  9.      String name;  
  10.      public StudentinfoRecord() {  
  11.   
  12.      }  
  13.      public void readFields(DataInput in) throws IOException {  
  14.         this.id = in.readInt();  
  15.         this.name = Text.readString(in);  
  16.      }  
  17.      public void write(DataOutput out) throws IOException {  
  18.         out.writeInt(this.id);  
  19.         Text.writeString(out, this.name);  
  20.      }  
  21.      public void readFields(ResultSet result) throws SQLException {  
  22.         this.id = result.getInt(1);  
  23.         this.name = result.getString(2);  
  24.      }  
  25.      public void write(PreparedStatement stmt) throws SQLException {  
  26.         stmt.setInt(1this.id);  
  27.         stmt.setString(2this.name);  
  28.      }  
  29.      public String toString() {  
  30.         return new String(this.id + " " + this.name);  
  31.      }  
  32.    }  
  33.    public class DBInputMapper extends MapReduceBase implements  
  34.         Mapper<LongWritable, StudentinfoRecord, LongWritable, Text> {  
  35.      public void map(LongWritable key, StudentinfoRecord value,  
  36.           OutputCollector<LongWritable, Text> collector, Reporter reporter)  
  37.           throws IOException {  
  38.         collector.collect(new LongWritable(value.id), new Text(value  
  39.              .toString()));  
  40.      }  
  41.    }  
  42.    public static void main(String[] args) throws IOException {  
  43.      JobConf conf = new JobConf(DBInput.class);  
  44.      DistributedCache.addFileToClassPath(new Path(  
  45.           "/lib/mysql-connector-java-5.1.0-bin.jar"), conf);  
  46.        
  47.      conf.setMapperClass(DBInputMapper.class);  
  48.      conf.setReducerClass(IdentityReducer.class);  
  49.   
  50.      conf.setMapOutputKeyClass(LongWritable.class);  
  51.      conf.setMapOutputValueClass(Text.class);  
  52.      conf.setOutputKeyClass(LongWritable.class);  
  53.      conf.setOutputValueClass(Text.class);  
  54.        
  55.      conf.setInputFormat(DBInputFormat.class);  
  56.      FileOutputFormat.setOutputPath(conf, new Path("/hua01"));  
  57.      DBConfiguration.configureDB(conf, "com.mysql.jdbc.Driver",  
  58.           "jdbc:mysql://192.168.3.244:3306/hadoop""hua""hadoop");  
  59.      String[] fields = { "id""name" };  
  60.      DBInputFormat.setInput(conf, StudentinfoRecord.class"studentinfo",  
  61.  null"id", fields);  
  62.   
  63.      JobClient.runJob(conf);  
  64.    }  
  65. }  

a)StudnetinfoRecord類的變數為表欄位,實現Writable和DBWritable兩個介面。

實現Writable的方法:

[java] view plaincopy
  1. public void readFields(DataInput in) throws IOException {  
  2.        this.id = in.readInt();  
  3.        this.name = Text.readString(in);  
  4.     }  
  5.     public void write(DataOutput out) throws IOException {  
  6.        out.writeInt(this.id);  
  7.        Text.writeString(out, this.name);  
  8.     }  

實現DBWritable的方法:

[java] view plaincopy
  1. public void readFields(ResultSet result) throws SQLException {  
  2.         this.id = result.getInt(1);  
  3.         this.name = result.getString(2);  
  4.      }  
  5.      public void write(PreparedStatement stmt) throws SQLException {  
  6.         stmt.setInt(1this.id);  
  7.         stmt.setString(2this.name);  
  8.      }  

b)讀入Mapper的value型別是StudnetinfoRecord。

c)配置如何連入資料庫,讀出表studentinfo資料。

[java] view plaincopy
  1. DBConfiguration.configureDB(conf, "com.mysql.jdbc.Driver",  
  2.           "jdbc:mysql://192.168.3.244:3306/hadoop""hua""hadoop");  
  3.      String[] fields = { "id""name" };  
  4.      DBInputFormat.setInput(conf, StudentinfoRecord.class"studentinfo",  null"id", fields);  


4、DBOutputFormat用法如下:
[java] view plaincopy
  1. public class DBOutput {  
  2.   
  3.    public static class StudentinfoRecord implements Writable,  DBWritable {  
  4.      int id;  
  5.      String name;  
  6.      public StudentinfoRecord() {  
  7.   
  8.      }  
  9.      public void readFields(DataInput in) throws IOException {  
  10.         this.id = in.readInt();  
  11.         this.name = Text.readString(in);  
  12.      }  
  13.      public void write(DataOutput out) throws IOException {  
  14.         out.writeInt(this.id);  
  15.         Text.writeString(out, this.name);  
  16.      }  
  17.      public void readFields(ResultSet result) throws SQLException {  
  18.         this.id = result.getInt(1);  
  19.         this.name = result.getString(2);  
  20.      }  
  21.      public void write(PreparedStatement stmt) throws SQLException {  
  22.         stmt.setInt(1this.id);  
  23.         stmt.setString(2this.name);  
  24.      }  
  25.      public String toString() {  
  26.         return new String(this.id + " " + this.name);  
  27.      }  
  28.    }  
  29.      
  30.    public static class MyReducer extends MapReduceBase implements  
  31.         Reducer<LongWritable, Text, StudentinfoRecord, Text> {  
  32.      public void reduce(LongWritable key, Iterator<Text> values,  
  33.           OutputCollector<StudentinfoRecord, Text> output, Reporter  reporter)  
  34.           throws IOException {  
  35.         String[] splits = values.next().toString().split("/t");  
  36.         StudentinfoRecord r = new StudentinfoRecord();  
  37.         r.id = Integer.parseInt(splits[0]);  
  38.         r.name = splits[1];  
  39.         output.collect(r, new Text(r.name));  
  40.      }  
  41.    }  
  42.   
  43.    public static void main(String[] args) throws IOException {  
  44.      JobConf conf = new JobConf(DBOutput.class);  
  45.      conf.setInputFormat(TextInputFormat.class);  
  46.      conf.setOutputFormat(DBOutputFormat.class);  
  47.   
  48.      FileInputFormat.setInputPaths(conf, new Path("/hua/hua.bcp"));  
  49.      DBConfiguration.configureDB(conf, "com.mysql.jdbc.Driver",  
  50.           "jdbc:mysql://192.168.3.244:3306/hadoop""hua""hadoop");  
  51.      DBOutputFormat.setOutput(conf, "studentinfo""id""name");  
  52.   
  53.   conf.setMapperClass(org.apache.hadoop.mapred.lib.IdentityMapper.class);  
  54.      conf.setReducerClass(MyReducer.class);  
  55.   
  56.      JobClient.runJob(conf);  
  57.    }  
  58.   
  59. }  

a)StudnetinfoRecord類的變數為表欄位,實現Writable和DBWritable兩個介面,同.DBInputFormat的StudnetinfoRecord類。

b)輸出Reducer的key/value型別是StudnetinfoRecord。

c)配置如何連入資料庫,輸出結果到表studentinfo。

[java] view plaincopy
  1. DBConfiguration.configureDB(conf, "com.mysql.jdbc.Driver",  
  2.           "jdbc:mysql://192.168.3.244:3306/hadoop""hua""hadoop");  
  3.      DBOutputFormat.setOutput(conf, "studentinfo""id""name");  

三、總結

      執行MapReduce時候報錯:java.io.IOException: com.mysql.jdbc.Driver,一般是由於程式找不到mysql驅動包。解決方法是讓每個

tasktracker執行MapReduce程式時都可以找到該驅動包。

新增包有兩種方式:

1.在每個節點下的${HADOOP_HOME}/lib下新增該包。重啟叢集,一般是比較原始的方法。

2.a)把包傳到叢集上: hadoop fs -put mysql-connector-java-5.1.0- bin.jar /lib

b)在mr程式提交job前,新增語句:istributedCache.addFileToClassPath(new Path("/lib/mysql- connector-java- 5.1.0-bin.jar"), conf);

3、雖然API用的是0.19的,但是使用0.20的API一樣可用,只是會提示方法已過時而已。

相關文章