官网地址: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底层的建议,采用一下步骤完成数据的导入。
loadtable.rb
to move the files into a new HBase table.运行HBase script loadtable。将文件移动到一个新的HBase表中本页面的其余部分将更详细地解释每个步骤。
限制条件:
除了处理这些约束之外,这里最重要的工作可能是决定如何为来自Hive的每一行分配一个HBase行键。为了避免词法比较器和二进制比较器之间的不一致,最简单的方法是设计一个字符串行键,并始终一致地使用它。如果要将多个列组合到键中,可以使用Hive的字符串concat表达式。你可以使用CREATE VIEW逻辑地添加你的rowkey,而不需要更新Hive中任何现有的数据。
根据实际情况自行确定。
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。同样,我们不知道文件的名称,所以我们复制它
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进行查看检查。
最后,如果你想访问你刚刚通过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");