【hbase】——HBase 写优化之 BulkLoad 实现数据快速入库
1、为何要 BulkLoad 导入?传统的 HTableOutputFormat 写 HBase 有什么问题?
我们先看下 HBase 的写流程:

通常 MapReduce 在写HBase时使用的是 TableOutputFormat 方式,在reduce中直接生成put对象写入HBase,该方式在大数据量写入时效率低下(HBase会block写入,频繁进行flush,split,compact等大量IO操作),并对HBase节点的稳定性造成一定的影响(GC时间过长,响应变慢,导致节点超时退出,并引起一系列连锁反应),而HBase支持 bulk load 的入库方式,它是利用hbase的数据信息按照特定格式存储在hdfs内这一原理,直接在HDFS中生成持久化的HFile数据格式文件,然后上传至合适位置,即完成巨量数据快速入库的办法。配合mapreduce完成,高效便捷,而且不占用region资源,增添负载,在大数据量写入时能极大的提高写入效率,并降低对HBase节点的写入压力。
通过使用先生成HFile,然后再BulkLoad到Hbase的方式来替代之前直接调用HTableOutputFormat的方法有如下的好处:
(1)消除了对HBase集群的插入压力
(2)提高了Job的运行速度,降低了Job的执行时间
目前此种方式仅仅适用于只有一个列族的情况,在新版 HBase 中,单列族的限制会消除。
2、bulkload 流程与实践
bulkload 方式需要两个Job配合完成: 
(1)第一个Job还是运行原来业务处理逻辑,处理的结果不直接调用HTableOutputFormat写入到HBase,而是先写入到HDFS上的一个中间目录下(如 middata) 
(2)第二个Job以第一个Job的输出(middata)做为输入,然后将其格式化HBase的底层存储文件HFile 
(3)调用BulkLoad将第二个Job生成的HFile导入到对应的HBase表中
下面给出相应的范例代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | importjava.io.IOException;importorg.apache.hadoop.conf.Configuration;importorg.apache.hadoop.fs.Path;importorg.apache.hadoop.hbase.HBaseConfiguration;importorg.apache.hadoop.hbase.KeyValue;importorg.apache.hadoop.hbase.client.HTable;importorg.apache.hadoop.hbase.client.Put;importorg.apache.hadoop.hbase.io.ImmutableBytesWritable;importorg.apache.hadoop.hbase.mapreduce.HFileOutputFormat;importorg.apache.hadoop.hbase.mapreduce.KeyValueSortReducer;importorg.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles;importorg.apache.hadoop.hbase.util.Bytes;importorg.apache.hadoop.io.IntWritable;importorg.apache.hadoop.io.LongWritable;importorg.apache.hadoop.io.Text;importorg.apache.hadoop.mapreduce.Job;importorg.apache.hadoop.mapreduce.Mapper;importorg.apache.hadoop.mapreduce.Reducer;importorg.apache.hadoop.mapreduce.lib.input.FileInputFormat;importorg.apache.hadoop.mapreduce.lib.input.TextInputFormat;importorg.apache.hadoop.mapreduce.lib.output.FileOutputFormat;importorg.apache.hadoop.mapreduce.lib.output.TextOutputFormat;importorg.apache.hadoop.util.GenericOptionsParser;publicclassGeneratePutHFileAndBulkLoadToHBase {    publicstaticclassWordCountMapper extendsMapper<LongWritable, Text, Text, IntWritable>    {        privateText wordText=newText();        privateIntWritable one=newIntWritable(1);        @Override        protectedvoidmap(LongWritable key, Text value, Context context)                throwsIOException, InterruptedException {            // TODO Auto-generated method stub            String line=value.toString();            String[] wordArray=line.split(" ");            for(String word:wordArray)            {                wordText.set(word);                context.write(wordText, one);            }                    }    }        publicstaticclassWordCountReducer extendsReducer<Text, IntWritable, Text, IntWritable>    {        privateIntWritable result=newIntWritable();        protectedvoidreduce(Text key, Iterable<IntWritable> valueList,                Context context)                throwsIOException, InterruptedException {            // TODO Auto-generated method stub            intsum=0;            for(IntWritable value:valueList)            {                sum+=value.get();            }            result.set(sum);            context.write(key, result);        }            }        publicstaticclassConvertWordCountOutToHFileMapper extendsMapper<LongWritable, Text, ImmutableBytesWritable, Put>    {        @Override        protectedvoidmap(LongWritable key, Text value, Context context)                throwsIOException, InterruptedException {            // TODO Auto-generated method stub            String wordCountStr=value.toString();            String[] wordCountArray=wordCountStr.split("\t");            String word=wordCountArray[0];            intcount=Integer.valueOf(wordCountArray[1]);                        //创建HBase中的RowKey            byte[] rowKey=Bytes.toBytes(word);            ImmutableBytesWritable rowKeyWritable=newImmutableBytesWritable(rowKey);            byte[] family=Bytes.toBytes("cf");            byte[] qualifier=Bytes.toBytes("count");            byte[] hbaseValue=Bytes.toBytes(count);            // Put 用于列簇下的多列提交,若只有一个列,则可以使用 KeyValue 格式            // KeyValue keyValue = new KeyValue(rowKey, family, qualifier, hbaseValue);            Put put=newPut(rowKey);            put.add(family, qualifier, hbaseValue);            context.write(rowKeyWritable, put);                    }            }        publicstaticvoidmain(String[] args) throwsException {        // TODO Auto-generated method stub        Configuration hadoopConfiguration=newConfiguration();        String[] dfsArgs = newGenericOptionsParser(hadoopConfiguration, args).getRemainingArgs();                //第一个Job就是普通MR,输出到指定的目录        Job job=newJob(hadoopConfiguration, "wordCountJob");        job.setJarByClass(GeneratePutHFileAndBulkLoadToHBase.class);        job.setMapperClass(WordCountMapper.class);        job.setReducerClass(WordCountReducer.class);        job.setOutputKeyClass(Text.class);        job.setOutputValueClass(IntWritable.class);        FileInputFormat.setInputPaths(job, newPath(dfsArgs[0]));        FileOutputFormat.setOutputPath(job, newPath(dfsArgs[1]));        //提交第一个Job        intwordCountJobResult=job.waitForCompletion(true)?0:1;                //第二个Job以第一个Job的输出做为输入,只需要编写Mapper类,在Mapper类中对一个job的输出进行分析,并转换为HBase需要的KeyValue的方式。        Job convertWordCountJobOutputToHFileJob=newJob(hadoopConfiguration, "wordCount_bulkload");                convertWordCountJobOutputToHFileJob.setJarByClass(GeneratePutHFileAndBulkLoadToHBase.class);        convertWordCountJobOutputToHFileJob.setMapperClass(ConvertWordCountOutToHFileMapper.class);        //ReducerClass 无需指定,框架会自行根据 MapOutputValueClass 来决定是使用 KeyValueSortReducer 还是 PutSortReducer        //convertWordCountJobOutputToHFileJob.setReducerClass(KeyValueSortReducer.class);        convertWordCountJobOutputToHFileJob.setMapOutputKeyClass(ImmutableBytesWritable.class);        convertWordCountJobOutputToHFileJob.setMapOutputValueClass(Put.class);                //以第一个Job的输出做为第二个Job的输入        FileInputFormat.addInputPath(convertWordCountJobOutputToHFileJob, newPath(dfsArgs[1]));        FileOutputFormat.setOutputPath(convertWordCountJobOutputToHFileJob, newPath(dfsArgs[2]));        //创建HBase的配置对象        Configuration hbaseConfiguration=HBaseConfiguration.create();        //创建目标表对象        HTable wordCountTable =newHTable(hbaseConfiguration, "word_count");        HFileOutputFormat.configureIncrementalLoad(convertWordCountJobOutputToHFileJob,wordCountTable);               //提交第二个job        intconvertWordCountJobOutputToHFileJobResult=convertWordCountJobOutputToHFileJob.waitForCompletion(true)?0:1;                //当第二个job结束之后,调用BulkLoad方式来将MR结果批量入库        LoadIncrementalHFiles loader = newLoadIncrementalHFiles(hbaseConfiguration);        //第一个参数为第二个Job的输出目录即保存HFile的目录,第二个参数为目标表        loader.doBulkLoad(newPath(dfsArgs[2]), wordCountTable);                //最后调用System.exit进行退出        System.exit(convertWordCountJobOutputToHFileJobResult);            }} | 
比如原始的输入数据的目录为:/rawdata/test/wordcount/20131212
中间结果数据保存的目录为:/middata/test/wordcount/20131212 
最终生成的HFile保存的目录为:/resultdata/test/wordcount/20131212 
运行上面的Job的方式如下: 
hadoop jar test.jar /rawdata/test/wordcount/20131212 /middata/test/wordcount/20131212 /resultdata/test/wordcount/20131212
3、说明与注意事项:
(1)HFile方式在所有的加载方案里面是最快的,不过有个前提——数据是第一次导入,表是空的。如果表中已经有了数据。HFile再导入到hbase的表中会触发split操作。
(2)最终输出结果,无论是map还是reduce,输出部分key和value的类型必须是: < ImmutableBytesWritable, KeyValue>或者< ImmutableBytesWritable, Put>。
否则报这样的错误:
| 1 2 3 | java.lang.IllegalArgumentException: Can't read partitions file...Caused by: java.io.IOException: wrong key class: org.apache.hadoop.io.*** is not classorg.apache.hadoop.hbase.io.ImmutableBytesWritable | 
(3)最终输出部分,Value类型是KeyValue 或Put,对应的Sorter分别是KeyValueSortReducer或PutSortReducer,这个 SorterReducer 可以不指定,因为源码中已经做了判断:
| 1 2 3 4 5 6 7 | if(KeyValue.class.equals(job.getMapOutputValueClass())) {    job.setReducerClass(KeyValueSortReducer.class);} elseif(Put.class.equals(job.getMapOutputValueClass())) {    job.setReducerClass(PutSortReducer.class);} else{    LOG.warn("Unknown map output value type:"+ job.getMapOutputValueClass());} | 
(4) MR例子中job.setOutputFormatClass(HFileOutputFormat.class); HFileOutputFormat只适合一次对单列族组织成HFile文件,多列簇需要起多个 job,不过新版本的 Hbase 已经解决了这个限制。
(5) MR例子中最后生成HFile存储在HDFS上,输出路径下的子目录是各个列族。如果对HFile进行入库HBase,相当于move HFile到HBase的Region中,HFile子目录的列族内容没有了。
(6)最后一个 Reduce 没有 setNumReduceTasks 是因为,该设置由框架根据region个数自动配置的。
(7)下边配置部分,注释掉的其实写不写都无所谓,因为看源码就知道configureIncrementalLoad方法已经把固定的配置全配置完了,不固定的部分才需要手动配置。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | publicclassHFileOutput {        //job 配置    publicstaticJob configureJob(Configuration conf) throwsIOException {        Job job = newJob(configuration, "countUnite1");        job.setJarByClass(HFileOutput.class);                //job.setNumReduceTasks(2);          //job.setOutputKeyClass(ImmutableBytesWritable.class);        //job.setOutputValueClass(KeyValue.class);        //job.setOutputFormatClass(HFileOutputFormat.class);         Scan scan = newScan();        scan.setCaching(10);        scan.addFamily(INPUT_FAMILY);        TableMapReduceUtil.initTableMapperJob(inputTable, scan,                HFileOutputMapper.class, ImmutableBytesWritable.class, LongWritable.class, job);        //这里如果不定义reducer部分,会自动识别定义成KeyValueSortReducer.class 和PutSortReducer.class                job.setReducerClass(HFileOutputRedcuer.class);        //job.setOutputFormatClass(HFileOutputFormat.class);        HFileOutputFormat.configureIncrementalLoad(job, newHTable(                configuration, outputTable));        HFileOutputFormat.setOutputPath(job, newPath());                //FileOutputFormat.setOutputPath(job, new Path()); //等同上句        returnjob;    }     publicstaticclassHFileOutputMapper extends            TableMapper<ImmutableBytesWritable, LongWritable> {        publicvoidmap(ImmutableBytesWritable key, Result values,                Context context) throwsIOException, InterruptedException {            //mapper逻辑部分            context.write(newImmutableBytesWritable(Bytes()), LongWritable());        }    }     publicstaticclassHFileOutputRedcuer extends            Reducer<ImmutableBytesWritable, LongWritable, ImmutableBytesWritable, KeyValue> {        publicvoidreduce(ImmutableBytesWritable key, Iterable<LongWritable> values,                Context context) throwsIOException, InterruptedException {                        //reducer逻辑部分            KeyValue kv = newKeyValue(row, OUTPUT_FAMILY, tmp[1].getBytes(),                    Bytes.toBytes(count));            context.write(key, kv);        }    }} | 
4、Refer:
1、Hbase几种数据入库(load)方式比较
http://blog.csdn.net/kirayuan/article/details/6371635
2、MapReduce生成HFile入库到HBase及源码分析
http://blog.pureisle.net/archives/1950.html
3、MapReduce生成HFile入库到HBase
http://shitouer.cn/2013/02/hbase-hfile-bulk-load/
【hbase】——HBase 写优化之 BulkLoad 实现数据快速入库的更多相关文章
- HBase 写优化之 BulkLoad 实现数据快速入库
		在第一次建立Hbase表的时候,我们可能需要往里面一次性导入大量的初始化数据.我们很自然地想到将数据一条条插入到Hbase中,或者通过MR方式等.但是这些方式不是慢就是在导入的过程的占用Region资 ... 
- IDEA中Spark往Hbase中写数据
		import org.apache.hadoop.hbase.HBaseConfiguration import org.apache.hadoop.hbase.io.ImmutableBytesWr ... 
- 如何降低90%Java垃圾回收时间?以阿里HBase的GC优化实践为例
		过去的一年里,我们准备在Ali-HBase上突破这个被普遍认知的痛点,为此进行了深度分析及全面创新的工作,获得了一些比较好的效果.以蚂蚁风控场景为例,HBase的线上young GC时间从120ms减 ... 
- 万字长文详解HBase读写性能优化
		一.HBase 读优化 1. HBase客户端优化 和大多数系统一样,客户端作为业务读写的入口,姿势使用不正确通常会导致本业务读延迟较高实际上存在一些使用姿势的推荐用法,这里一般需要关注四个问题: 1 ... 
- 一个自定义 HBase Filter -“通过RowKeys来高性能获取数据”
		摘要: 大家在使用HBase和Solr搭建系统中经常遇到的一个问题就是:“我通过SOLR得到了RowKeys后,该怎样去HBase上取数据”.使用现有的Filter性能差劲,网上也没有现成的自定义Fi ... 
- HBase并行写机制(mvcc)
		HBase在保证高性能的同时,为用户提供了便于理解的一致性数据模型MVCC (Multiversion Concurrency Control),即多版本并发控制技术,把数据库的行锁与行的多个版本结合 ... 
- hbase G1 GC优化
		本文借鉴之前HBaseConAsia2017,小米公司对hbase g1 gc的优化分享.此外还可以参考apache官方博客对于hbase g1 gc优化的一篇文章(Tuning G1GC For Y ... 
- Hbase实用技巧:全量+增量数据的迁移方法
		摘要:本文介绍了一种Hbase迁移的方法,可以在一些特定场景下运用. 背景 在Hbase使用过程中,使用的Hbase集群经常会因为某些原因需要数据迁移.大多数情况下,可以跟用户协商用离线的方式进行迁移 ... 
- 使用MapReduce查询Hbase表指定列簇的全部数据输出到HDFS(一)
		package com.bank.service; import java.io.IOException; import org.apache.hadoop.conf.Configuration;im ... 
随机推荐
- 数据操作语言DML与运算符
			数据操作语言DML(添加,修改,删除) 1.添加数据 insert into insert into 表名 (字段列表) values (值列表),值列表要和字段列表按顺序匹配. insert int ... 
- C语言Scanf函数
			C语言的scanf函数 一.变量的内存分析 (一)字节与地址 ①. 内存以字节为单位 每个字节都有自己的内存地址,根据地址就可以找到该字节.整个内存相当于一整个酒店,而酒店以房间为单位,在这里每个房间 ... 
- Web Service代理类生成工具
			本文原文连接:http://www.cnblogs.com/dengxinglin/p/3334158.html 之前一篇文章写 Web Service服务代理类生成及编译 , 通过命令行的方式可以直 ... 
- linux下安装rzsz
			1.登陆linux,下载rzsz安装包 wget http://freeware.sgi.com/source/rzsz/rzsz-3.48.tar.gz 2.tar zxvf rzsz-3.48.t ... 
- java集合-HashTable
			概述 和 HashMap 一样,Hashtable 也是一个散列表,它存储的内容是键值对. Hashtable 在 Java 中的定义为: public class Hashtable<K,V& ... 
- 钉钉客户端JS-API权限签名算法.NET版
			前段时间写了一篇博文<钉钉如何进行PC端开发>,在里面并未解决本地生成签名的问题,需要到官网进行生成,由于钉钉门票等认证信息会超期,因此,必须能本地用代码自动更新相关参数信息,来换取签名. ... 
- 如何停止CSS3的动画?
			前言 我们在移动端一般使用zepto框架,与其说zepto是jquery的轻量级替代版,不如说是html5替代版我们在js中会用到animate方法执行动画,这个家伙可是真资格的动画,完全是css一点 ... 
- ruby(&gem) koala安装
			1.ruby下载安装 下载地址:http://railsinstaller.org/en 选择合适版本 2.gem(安装ruby时自带gem) 删除原镜像:gem sources --remove h ... 
- 原生JS实现轮播+学前端的感受(防止走火入魔)
			插件!插件!天天听到有人求这个插件,那个插件的,当然,用第三方插件可以大幅提高开发效率,但作为新手,我还是喜欢自己来实现,主要是我有时间! 今天我来给大家分享下用原生JS实现图片轮播的写法 前辈们可以 ... 
- 每日一博 | 用 Ionic2 创建 App 启动页滑动欢迎界面
			原文 https://my.oschina.net/qinphil/blog/777787 效果如下,图片来自网络 本文例子和上图稍有不同,主要功能如下: 每滑动一下展示一张全屏图片: 滑动到最后一 ... 
