日常開發中可能會碰到需要編寫MapReduce從HDFS上讀取數據,然後導入HBase。一般會使用到兩種方式,下面分別介紹下。
第一種方式:
指定OutputFormatClass爲TableOutputFormat,構造Put對象,然後設置到OutputValueClass去。
Configuration conf = ConfSource.getHBaseConf();
Job j = new Job(conf, "Import table " + tbName + " into hbase table:bigtable from " + path);
j.setMapperClass(Sync2HBaseMapper.class);
j.setOutputFormatClass(TableOutputFormat.class);
j.getConfiguration().set(TableOutputFormat.OUTPUT_TABLE, "bigtable");
j.setOutputKeyClass(ImmutableBytesWritable.class);
j.setOutputValueClass(Put.class);
j.setNumReduceTasks(0);
j.setJarByClass(Sync2HBaseJob.class);
但是,這種寫法在數據量大、節點比較多的情況效率不太好。Reduce節點的輸出在MapReduce運行過程中不斷導入到HBase,會造成很大的網絡開銷,而且事務控制也是難點,所以,只是在數據量較少的情況下可以使用該方法。
第二種方式:
使用HFileOutputFormat2類生成HFile, HFile是HBase中KeyValue數據的存儲格式,Hadoop的二進制格式文件,實際上StoreFile就是對HFile做了輕量級包裝,即StoreFile底層就是HFile。生成的HFile會放置在指定的HDFS目錄下,然後是使用completebulkload命令就可以快速地導入到HBase,相對跑MapReduce的時間,completebulkload的執行時間幾乎可以忽略不計,本人在16核,128G內存的機器下,600M的數據源MapReduce跑了20分鐘,而使用completebulkload導入HBase只需要幾秒,非常快。但是要注意的是運行completebulkload後,HDFS上的HFile會被自動刪除掉,最好做下備份。
Configuration conf = ConfSource.getHBaseConf();
Job job = new Job(conf, "Import into hbase table"
+ confClz.getHbaseTable() + " from "
+ confClz.getDownloadPath());
job.setJarByClass(Sync2HBaseJobViaHFile.class);
FileInputFormat.setInputPaths(job, confClz.getDownloadPath());
job.setMapperClass(Sync2HBaseMapper.class);
HTable table = new HTable(conf, confClz.getHbaseTable());
job.setReducerClass(PutSortReducer.class);
Path outputDir = new Path(confClz.getHfilePath());
FileOutputFormat.setOutputPath(job, outputDir);
job.setMapOutputKeyClass(ImmutableBytesWritable.class);
job.setMapOutputValueClass(Put.class);
HFileOutputFormat2.configureIncrementalLoad(job, table);
TableMapReduceUtil.addDependencyJars(job);