Hive使用HBaseBulkLoad导入Hbase

官网地址:HBaseBulkLoad - Apache Hive - Apache Software Foundation 

概述:

如果数据量比较小,可以使用Hive和Hbase集成的方式(HBaseIntegration)完成数据的导入,同时通过Hive读取数据。集成方式如下:

CREATE TABLE new_hbase_table(rowkey string, x int, y int)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,cf:x,cf:y");
 
SET hive.hbase.bulk=true;
 
INSERT OVERWRITE TABLE new_hbase_table
SELECT rowkey_expression, x, y FROM ...any_hive_query...;

如果数据量比较大时,批量数据的导入会影响数据的读写,为此可以基于Hbase底层的建议,采用一下步骤完成数据的导入。       

  • Decide how you want the data to look once it has been loaded into HBase.决定数据加载到Hbase后的查看方式。
  • Decide on the number of reducers you're planning to use for parallelizing the sorting and HFile creation. This depends on the size of your data as well as cluster resources available. 根据数据量的大小以及集群的资源情况,设置平行排序和HFile创建的reducer数量
  • Run Hive sampling commands which will create a file containing "splitter" keys which will be used for range-partitioning the data during sort.使用Hive命令对数据集进行采样,生成分区键文件,用于在排序期间对数据进行范围分区。
  • Prepare a staging location in HDFS where the HFiles will be generated.在HDFS中准备一个将要生成hfile的暂存位置。
  • Run Hive commands which will execute the sort and generate the HFiles.运行Hive命令,执行排序并生成hfile。
  • (Optional: if HBase and Hive are running in different clusters, distcp the generated files from the Hive cluster to the HBase cluster.) 可选项:如果Hbase和Hive不在一个集群中,需要将生成的文件从Hive集群distcp到HBase集群。
  • Run HBase script loadtable.rb to move the files into a new HBase table.运行HBase script loadtable。将文件移动到一个新的HBase表中
  • (Optional: register the HBase table as an external table in Hive so you can access it from there.)可选:在Hive中将HBase表注册为外部表,以便从外部访问。

本页面的其余部分将更详细地解释每个步骤。

HBase限制条件

限制条件:

  • 目标表必须是一个空表
  • 目标表只能有一个列族
  • 目标表不能是稀疏的(每一行将有相同的列集);

除了处理这些约束之外,这里最重要的工作可能是决定如何为来自Hive的每一行分配一个HBase行键。为了避免词法比较器和二进制比较器之间的不一致,最简单的方法是设计一个字符串行键,并始终一致地使用它。如果要将多个列组合到键中,可以使用Hive的字符串concat表达式。你可以使用CREATE VIEW逻辑地添加你的rowkey,而不需要更新Hive中任何现有的数据。

估计所需要的资源

根据实际情况自行确定。

添加必要的Jar

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

Then add them to your hive-site.xml:



  hive.aux.jars.path
  /user/hive/hbase-VERSION.jar,/user/hive/hive-hbase-handler-VERSION.jar

当然也可以通过Hive client中动态指定加载的jar包。

准备分区范围 

防止数据倾斜,根据数据生成分区键文件。

为了对数据执行并行排序,我们需要对其进行范围分区。其思想是将行键的空间划分为几乎相等大小的范围,在并行排序中使用每个reducer。细节会根据你的源数据而变化,你可能需要运行一些探索性的Hive查询来得出一个足够好的范围集。这里有一个例子:

add jar lib/hive-contrib-0.7.0.jar;
set mapred.reduce.tasks=1;
create temporary function row_sequence as
'org.apache.hadoop.hive.contrib.udf.UDFRowSequence';
select transaction_id from
(select transaction_id
from transactions
tablesample(bucket 1 out of 10000 on transaction_id) s
order by transaction_id
limit 10000000) x
where (row_sequence() % 910000)=0
order by transaction_id
limit 11;

这是通过对表的0.01%样本中的所有行进行排序(使用单个reducer),然后选择第n行(这里n=910000)来实现的。n的值是通过将样本中的总行数除以所需的范围数来选择的,例如本例中的12(比LIMIT子句产生的分区键数多一个)。这里的假设是样本中的分布与表中的总体分布相匹配;如果不是这样,则生成的分区键将导致并行排序出现倾斜。

一旦定义了抽样查询,下一步就是将其结果保存到一个格式正确的文件中,该文件将在后续步骤中使用。运行如下命令:

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';
 
insert overwrite table hb_range_keys
select transaction_id from
(select transaction_id
from transactions
tablesample(bucket 1 out of 10000 on transaction_id) s
order by transaction_id
limit 10000000) x
where (row_sequence() % 910000)=0
order by transaction_id
limit 11;

第一个命令创建一个外部表,定义要创建的文件的格式;确保完全按照指定的方式设置serde和inputformat/outputformat。

第二个命令生成文件(使用前面定义的抽样查询)。使用order by 进行排序,最终在目录/tmp/hb_range_keys中生成单个文件。文件名是未知的,但是以后需要通过文件名引用该文件,所以运行如下命令将其复制到一个特定的名称

dfs -cp /tmp/hb_range_keys/* /tmp/hb_range_key_list;

创建任务执行临时目录

排序将产生大量的数据,因此请确保在HDFS集群中有足够的空间,并选择文件存放的位置。在本例中,我们将使用/tmp/hbsort

目录实际上不需要存在(它将在下一步中自动创建),但是如果它存在,它应该是空的。

dfs -rmr /tmp/hbsort;
dfs -mkdir /tmp/hbsort;

数据排序

现在迈出了重要的一步:对所有要批量加载的数据运行排序。确保Hive实例中包含Hbase的jar.

set hive.execution.engine=mr;
set mapred.reduce.tasks=12;
set hive.mapred.partitioner=org.apache.hadoop.mapred.lib.TotalOrderPartitioner;
set total.order.partitioner.path=/tmp/hb_range_key_list;
set hfile.compression=gz;
 
create table hbsort(transaction_id string, user_name string, amount double, ...)
stored as
INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.hbase.HiveHFileOutputFormat'
TBLPROPERTIES ('hfile.family.path' = '/tmp/hbsort/cf');
 
insert overwrite table hbsort
select transaction_id, user_name, amount, ...
from transactions
cluster by transaction_id;

CREATE TABLE创建一个虚拟表,它控制如何写入排序的输出。注意,它使用HiveHFileOutputFormat来完成此操作,表属性hfile.family.path用于控制输出的目标目录。同样,确保完全按照指定的方式设置inputformat/outputformat。在上面的例子中,我们选择gzip (gz)压缩的结果文件;如果不设置hfile.compression参数,则不会执行压缩操作。(另一种可用的方法是lzo,它的压缩力度较小,但不需要那么多CPU功耗。)

注意reduce任务的数量比分区的数量多一个,否则你会得到一个“Wrong number of partitions in keyset”的错误。


有一个参数hbase.hregion.max.filesize(默认256MB)影响如何生成hfile。如果一个reducer产生的数据量(预压缩)超过了这个限制,就会为这个reducer生成多个HFile。这将导致区域文件不平衡。这不会导致任何正确性问题,但如果你想获得均衡的区域文件,要么使用更多的reducer,要么将这个参数值设置的更大一些。

路径中的cf指定了将在HBase中创建的列族的名称,因此这里选择的目录名很重要。(注意,这里我们并没有实际使用HBase表,根据实际情况设定)

CLUSTER BY子句提供了分区程序使用的键;确保它与前面步骤中提出的范围分区相匹配。

SELECT列表中的第一列被解释为rowkey;随后的列成为单元格值(所有列都在一个列族中,因此它们的列名很重要)。

开始执行Hbase脚本

排序作业成功完成后,还需要最后一步将结果文件导入HBase。同样,我们不知道文件的名称,所以我们复制它

dfs -copyToLocal /tmp/hbsort/cf/* /tmp/hbout

如果Hive和HBase运行在不同的集群中,可以使用distcp将文件从一个集群复制到另一个集群。

如果Hive和HBase运行在不同的集群中,可以使用distcp将文件从一个集群复制到另一个集群。

hadoop jar hbase-VERSION.jar completebulkload [-c /path/to/hbase/config/hbase-site.xml] /tmp/hbout transactions

如果是老版本,使用以下命令

hbase org.jruby.Main loadtable.rb transactions /tmp/hbout

第一个参数(transactions)指定了新HBase表的名称。对于第二个参数,传递临时目录名,而不是列族子目录

脚本执行后耐心等待,可用hbase shell进行查看检查。

Hbase映射回Hive

最后,如果你想访问你刚刚通过Hive创建的HBase表:

CREATE EXTERNAL TABLE hbase_transactions(transaction_id string, user_name string, amount double, ...)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,cf:user_name,cf:amount,...")
TBLPROPERTIES("hbase.table.name" = "transactions");

Followups Needed

  • Support sparse tables
  • Support loading binary data representations once HIVE-1245 is fixed
  • Support assignment of timestamps
  • Provide control over file parameters such as compression
  • Support multiple column families once HBASE-1861 is implemented
  • Support loading into existing tables once HBASE-1923 is implemented
  • Wrap it all up into the ideal single-INSERT-with-auto-sampling job...

你可能感兴趣的:(hive,hbase,大数据)