HBase與MapReduce整合

一、如果在MapReuduce中使用HBase的API操作HBase時出現下面的錯誤

Error: java.lang.ClassNotFoundException: org.apache.hadoop.hbase.HBaseConfiguration

解決方式一:

在yarn的時候添加jar包的另一種方式

export libpath=/path to hbase jar

hadoop jar myjarname.jar <classpath> arguments -libjar ${libpath}

解決方式二:

hbase-site.xml添加到$ HADOOP_HOME / conf並且把HBase lib中的jar包都拷貝到Hadoop lib下

解決方式三:

把HBase的jar包整合進Hadoop ClassPath,在hadoop-env.sh加上

export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:/opt/soft/hbase/lib/*

遺憾的是我試了上面方式,結果都失敗了!

HBase和MapReduce的整合有官方方式,如果在Map類中或者Reduce類寫對HBase的操作會瘋狂報上面的錯誤,我還沒找到解決的辦法,原因的話我猜是AppMstr把map task和reduce task打包到Container中運行的時候並沒有把相應的jar包也打包。不過官方文檔中給出的原因是默認情況下,部署到MapReduce集羣的MapReduce作業無權訪問$ HBASE_CONF_DIR下的HBase配置或HBase類。

二、官方整合方式

注意點:

1、HBase可以作爲MapReduce的數據源或者數據接收器。

2、在編寫讀取或寫入HBase的MapReduce Job時,建議繼承TableMpper或者TableReducer。

3、mapreduce.job.maps的數量要大於HBase中Region的數量。

4、在插入HBase的時候會對數據進行一次排序,所以不需要在Reduce中再進行排序。

5、如果job邏輯不需要Reduce進行操作可以把Reduce的數量設置爲0

6、HBase和Hive的依賴Jar包中有很多Jar包和Spark的Jar包衝突(相同名字不同版本)

官方示例一:從HBase中的一張表中的數據複製到另一張表中

Configuration config = HBaseConfiguration.create();
Job job = new Job(config,"ExampleReadWrite");
job.setJarByClass(MyReadWriteJob.class);    

Scan scan = new Scan();
scan.setCaching(500);        // 默認掃描數爲1,但這對於MR作業來說是不好的
scan.setCacheBlocks(false);  // 對於MR作業要設置爲false
// 還可以設置其他Scan參數

TableMapReduceUtil.initTableMapperJob(
  sourceTable,      // 輸入表
  scan,             // Scan實例用來控制列族和屬性的選擇
  MyMapper.class,   // mapper class
  null,             // mapper輸出key的class
  null,             // mapper輸出value的class
  job);
TableMapReduceUtil.initTableReducerJob(
  targetTable,      // 輸出表
  null,             // reducer class
  job);
job.setNumReduceTasks(0);

boolean b = job.waitForCompletion(true);
if (!b) {
    throw new IOException("error with job!");
}

這裏TableMapReduceUtil的作用是設置outputFormat類爲TableOutputFormat ,並在配置上設置了幾個參數(比如TableOutputFormat.OUTPUT_TABLE),以及將reducer輸出key設置爲ImmutableBytesWritable和reducer輸出value爲Writable。這些設置都可以在conf中設置,TableMapReduceUtil只是把這件事情變得更容易。

public static class MyMapper extends TableMapper<ImmutableBytesWritable, Put>  {

  public void map(ImmutableBytesWritable row, Result value, Context context) throws IOException, InterruptedException {
    // 這個例子只是從源表中Copy數據
      context.write(row, resultToPut(row,value));
    }

    private static Put resultToPut(ImmutableBytesWritable key, Result result) throws IOException {
      Put put = new Put(key.get());
      for (KeyValue kv : result.raw()) {
        put.add(kv);
      }
      return put;
    }
}

多表輸出 MultiTableOutputFormat

官方示例二: 對錶中的某一列數據計數並寫入另一個表中

Configuration config = HBaseConfiguration.create();
Job job = new Job(config,"ExampleSummary");
job.setJarByClass(MySummaryJob.class);     

Scan scan = new Scan();
scan.setCaching(500);        
scan.setCacheBlocks(false);  


TableMapReduceUtil.initTableMapperJob(
  sourceTable,        
  scan,               
  MyMapper.class,     
  Text.class,         
  IntWritable.class,  
  job);
TableMapReduceUtil.initTableReducerJob(
  targetTable,        
  MyTableReducer.class,    
  job);
job.setNumReduceTasks(1);   // 設置Reduce的數量爲1

boolean b = job.waitForCompletion(true);
if (!b) {
  throw new IOException("error with job!");
}

Mapper:

public static class MyMapper extends TableMapper<Text, IntWritable>  {
  public static final byte[] CF = "cf".getBytes();
  public static final byte[] ATTR1 = "attr1".getBytes();

  private final IntWritable ONE = new IntWritable(1);
  private Text text = new Text();

  public void map(ImmutableBytesWritable row, Result value, Context context) throws IOException, InterruptedException {
    String val = new String(value.getValue(CF, ATTR1));
    text.set(val);     
    context.write(text, ONE);
  }
}

Reducer:

public static class MyTableReducer extends TableReducer<Text, IntWritable, ImmutableBytesWritable>  {
  public static final byte[] CF = "cf".getBytes();
  public static final byte[] COUNT = "count".getBytes();

  public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
    int i = 0;
    for (IntWritable val : values) {
      i += val.get();
    }
    Put put = new Put(Bytes.toBytes(key.toString()));
    put.add(CF, COUNT, Bytes.toBytes(i));

    context.write(null, put);
  }
}

 官方示例三: 對錶中的某一列數據計數並寫入HDFS

Configuration config = HBaseConfiguration.create();
Job job = new Job(config,"ExampleSummaryToFile");
job.setJarByClass(MySummaryFileJob.class);     

Scan scan = new Scan();
scan.setCaching(500);        
scan.setCacheBlocks(false);  

TableMapReduceUtil.initTableMapperJob(
  sourceTable,        
  scan,               
  MyMapper.class,     
  Text.class,         
  IntWritable.class,  
  job);
job.setReducerClass(MyReducer.class);    
job.setNumReduceTasks(1);    
FileOutputFormat.setOutputPath(job, new Path("/tmp/mr/mySummaryFile"));  // 根據需要調整目錄
boolean b = job.waitForCompletion(true);
if (!b) {
  throw new IOException("error with job!");
}

這裏的Mapper和示例二中的一致,至於Reducer,它是一個“通用”Reducer而不是擴展TableMapper併發出Puts。

public static class MyReducer extends Reducer<Text, IntWritable, Text, IntWritable>  {

  public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
    int i = 0;
    for (IntWritable val : values) {
      i += val.get();
    }
    context.write(key, new IntWritable(i));
  }
}

 官方示例四: 將計數結果寫入到RDBMS

可以在生命週期方法setup中建立連接,在cleanup中關閉連接,需要注意的是作業中的Reduce越多建立的連接也越多。

public static class MyRdbmsReducer extends Reducer<Text, IntWritable, Text, IntWritable>  {

  private Connection c = null;

  public void setup(Context context) {
    // 建立數據庫連接
  }

  public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
    // 在這個例子中key是Text
  }

  public void cleanup(Context context) {
    // 關閉數據庫連接
  }

}

 官方示例五: 在MR作業中訪問其他HBase表

通過在Mapper的setup方法中創建Table實例,可以在MapReduce作業中將其他HBase表作爲查找表等進行訪問。

public class MyMapper extends TableMapper<Text, LongWritable> {
  private Table myOtherTable;

  public void setup(Context context) {
    // 在這裏創建一個到集羣的連接,並保存它,或者使用連接
    // 來自於以存在的表
    myOtherTable = connection.getTable("myOtherTable");
  }

  public void map(ImmutableBytesWritable row, Result value, Context context) throws IOException, InterruptedException {
    // 處理結果...
    // 使用 'myOtherTable' 查找
  }

示例六: 將HDFS上的文件寫入HBase表中(Map沒有繼承TableMapper的方式)

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {

        Configuration config = HBaseConfiguration.create();
        config.set("hbase.zookeeper.quorum", "master,slave01,slave02");
        Job job = Job.getInstance(config, CountryToHBase.class.getName());
        job.setMapperClass(MyMapper.class);
        job.setMapOutputKeyClass(NullWritable.class);
        job.setMapOutputValueClass(Put.class);
        //只有map沒有reduce,所以設置reduce的數目爲0
        job.setNumReduceTasks(0);
        //設置數據的輸入路徑,沒有使用參數,直接在程序中寫入HDFS的路徑
        FileInputFormat.setInputPaths(job, new Path("/output/weather/country_weather/part-r-00000"));
        Connection connection = ConnectionFactory.createConnection(config);
        Admin admin = connection.getAdmin();
        //創建表
        TableName tb_name = TableName.valueOf("country_weather");
        HTableDescriptor desc = new HTableDescriptor(tb_name);
        HColumnDescriptor cf = new HColumnDescriptor("field");
        //設置列族
        desc.addFamily(cf);
        admin.createTable(desc);
        //驅動函數
        TableMapReduceUtil.initTableReducerJob("country_weather",null, job);
        TableMapReduceUtil.addDependencyJars(job);
        job.setJarByClass(MyMapper.class);
        job.waitForCompletion(true);


    }

    private static class MyMapper extends Mapper<LongWritable, Text, NullWritable, Put> {
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String[] splits = value.toString().split(" ");
            Put put = new Put( key.toString().getBytes());
            put.addColumn("field".getBytes(),"province".getBytes(),splits[0].getBytes());
            put.addColumn("field".getBytes(),"adcode".getBytes(),splits[1].getBytes());
            put.addColumn("field".getBytes(),"longitude".getBytes(),splits[2].getBytes());
            put.addColumn("field".getBytes(),"latitude".getBytes(),splits[3].getBytes());
            put.addColumn("field".getBytes(),"year".getBytes(),splits[4].getBytes());
            put.addColumn("field".getBytes(),"month".getBytes(),splits[5].getBytes());
            put.addColumn("field".getBytes(),"tem".getBytes(),splits[6].getBytes());
            put.addColumn("field".getBytes(),"rhu".getBytes(),splits[7].getBytes());
            put.addColumn("field".getBytes(),"ssd".getBytes(),splits[8].getBytes());
            put.addColumn("field".getBytes(),"pre".getBytes(),splits[9].getBytes());
            context.write(NullWritable.get(), put);
        }
    }

MapReduce替代API Cascading

實際上使用的還是MapReduce,但可以用簡化的方式編寫MapReduce代碼

下面例子表示將數據寫入HBase,同樣也可以從HBase獲取數據

// 從默認的文件系統讀數據
// 提交兩個字段: "offset" 和 "line"
Tap source = new Hfs( new TextLine(), inputFileLhs );

// 在一個HBase集羣中存儲數據
// 接收字段 "num", "lower", and "upper"
// 自動將傳入字段的範圍擴展到它們的正確家族名稱, "left" 或 "right"
Fields keyFields = new Fields( "num" );
String[] familyNames = {"left", "right"};
Fields[] valueFields = new Fields[] {new Fields( "lower" ), new Fields( "upper" ) };
Tap hBaseTap = new HBaseTap( "multitable", new HBaseScheme( keyFields, familyNames, valueFields ), SinkMode.REPLACE );

// 用於解析輸入字段的簡單管道程序集
// 一個真實的應用程序可能將多個管道鏈接在一起,以便進行更復雜的處理
Pipe parsePipe = new Each( "insert", new Fields( "line" ), new RegexSplitter( new Fields( "num", "lower", "upper" ), " " ) );

// "plan" 是一個集羣可執行流
// 將source Tap和hBaseTap(sink Tap)連接到parsePipe
Flow parseFlow = new FlowConnector( properties ).connect( source, hBaseTap, parsePipe );

// 啓動流,並阻塞直到完成
parseFlow.complete();

// 打開在HBase表中填充數據的迭代器
TupleEntryIterator iterator = parseFlow.openSink();

while(iterator.hasNext())
  {
  // 從HBase打印出每個元組
  System.out.println( "iterator.next() = " + iterator.next() );
  }

iterator.close();

參考文章:http://hbase.apache.org/1.2/book.html#mapreduce.htable.access

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