本文主要参考了hbase和hive官方文档的说明,并结合cdh和hdp的一些教程以及个人在生产中的实践进行记录。主要内容有hbase bulkload的原理以及对应hive的操作步骤,最后基于cdh进行完整实验提供参考实例。
不过整个操作确实很复杂繁琐,不是很建议使用。现在有挺多使用Spark Bulkload,下次有机会尝试一下。
之前是遇到一个需求,源表在hbase上,需要重新生成rowkey并提取部分字段形成新表。功能很简单,就是行对行映射过去,但是效率太低耗时太长,用bulkload确实解决了当时的麻烦。
写这篇文章是对自己操作的复盘,同时也梳理一下知识。
实验环境为:CDH6.3.2,对应的各个组件版本为:hadoop3.0.0,hbase2.1.0,hive2.1.1
这里推荐阅读hbase 最新官方文档:http://hbase.apache.org/2.3/book.html#arch.bulk.load
注意官方文档提供了2.2版本和2.3版本,这里原理讲解使用的是最新的,具体操作需结合根据实际使用版本来,有一些细节可能存在差异,例如2.2和2.3版本最终bulkload使用的jar包名称不一样。
这里简单地机翻一下:
HBase包括几种将数据加载到表中的方法。 最直接的方法是使用MapReduce任务中的TableOutputFormat类,或使用常规客户端API。 但是,这些方法并不总是最有效的方法。
批量加载功能使用MapReduce任务以HBase的内部数据格式输出表数据,然后将生成的StoreFiles直接加载到正在运行的集群中。 与通过HBase API进行加载相比,使用批量加载将占用更少的CPU和网络资源。
HBase批量加载过程包括两个主要步骤。
批量加载的第一步是使用HFileOutputFormat2从MapReduce任务生成HBase数据文件(存储文件)。这种输出格式以HBase的内部存储格式写出数据,以便稍后可以有效地将它们加载到集群中。
为了有效地运行,必须配置HFileOutputFormat2,使每个输出的HFile都适用于单个region。为此,那些 输出结果将被批量加载到HBase中 的MapReduce任务,使用 Hadoop 的 TotalOrderPartitioner 类将map输出划分为 key 空间的不相交范围,该范围与表中 region 的 key 范围相对应。
HFileOutputFormat2包括一个便捷函数configureIncrementalLoad(),该函数根据表的当前区域边界自动设置TotalOrderPartitioner。
在准备好要导入的数据之后,通过使用具有 “importtsv.bulk.output” 选项的 importtsv 工具,或通过使用 HFileOutputFormat 的 MapReduce 作业,可以使用 completebulkload 工具将数据导入到正在运行的集群中。 此命令行工具遍历准备好的数据文件,并为每个文件确定文件所属的region。 然后,它将连接上 HFile 对应的合适的 RegionServer ,将其移入其存储目录并将数据提供给客户端。
如果region边界在批量加载准备过程中或在准备和完成步骤之间发生了更改,则completebulkload程序将自动将数据文件分割为与新边界对应的部分。这个过程效率不高,所以用户应该尽量减少准备批量加载和将其导入集群之间的延迟,特别是当其他客户机通过其他方式同时加载数据时。
hadoop jar hbase-mapreduce-VERSION.jar completebulkload [-c /path/to/hbase/config/hbase-site.xml] /user/todd/myoutput mytable
如果类路径中没有提供相应的hbase参数,那么-c config-file选项可以用来指定包含相应hbase参数的文件(例如hbase-site.xml)(另外,如果zookeeper不是由hbase管理的,那么类路径必须包含包含zookeeper配置文件的目录)。
这里主要参考hive的文档:https://cwiki.apache.org/confluence/display/Hive/HBaseBulkLoad
这里需要明确最终导入的hbase表的几个限制条件:
实测这一步可跳过,不如自己在hive shell上 add jar 。个人感觉hive.aux.jars.path 这个配置比较鸡肋。又不支持直接指定文件夹,发现少了jar包后还得再去编辑文件,需要的jar包一多xml看起来就很恶心。
hadoop dfs -put /usr/lib/hive/lib/hbase-VERSION.jar /user/hive/hbase-VERSION.jar
hadoop dfs -put /usr/lib/hive/lib/hive-hbase-handler-VERSION.jar /user/hive/hive-hbase-handler-VERSION.jar
<property>
<name>hive.aux.jars.path</name>
<value>/user/hive/hbase-VERSION.jar,/user/hive/hive-hbase-handler-VERSION.jar</value>
</property>
为了最后bulk load的时候可以使用多个reducer加载到多个region,我们需要根据rowkey预先划分好数据的分区范围。这个范围将以文件的形式提供给下一步的中间hive表。这一步的目的就是要得到这个分区文件。
分区文件以 org.apache.hadoop.hive.ql.io.HiveNullValueSequenceFileOutputFormat outputformat格式存在于hdfs上。我们可以创建一个分区信息表,指定好该表的存储位置,这样该位置上的文件即为我们要的分区信息文件。
分区信息表的创建如下:
create external table hb_range_keys(transaction_id_range_start string)
row format serde
'org.apache.hadoop.hive.serde2.binarysortable.BinarySortableSerDe'
stored as
inputformat
'org.apache.hadoop.mapred.TextInputFormat'
outputformat
'org.apache.hadoop.hive.ql.io.HiveNullValueSequenceFileOutputFormat'
location '/tmp/hb_range_keys';
这一步需要插入rowkey的边界。例如,插入数据为 005,则最终生成的hbase的region有两个:下边界005、005上边界。
边界的选择很重要,良好的预分区设计可以减少hbase再平衡的压力,在大数据量下很有用。
因为我们的源表