Hive HBase 集成

Version information

HBase 列中存储的 Avro 数据

从 Hive 0.9.0 开始,HBase 集成至少需要 HBase 0.92,较早版本的 Hive 仍在使用 HBase 0.89/0.90

Version information

Hive 1.x 将 continue 与 HBase 0.98.x 和更低版本兼容。 Hive 2.x 将与 HBase 1.x 及更高版本兼容。 (有关详细信息,请参见HIVE-10990。)想要使用 Hive 1.x 使用 HBase 1.x 的 Consumer 将需要自己编译 Hive 1.x 流代码。

Introduction

本页记录了最初在HIVE-705中引入的 Hive/HBase 集成支持。此功能允许 Hive QL 语句访问HBase表以进行读取(SELECT)和写入(INSERT)。甚至可以通过联接和联合将对 HBase 表的访问与本机 Hive 表结合。

HBase HUG10 聚会提供了演示文稿

此功能正在进行中,欢迎对其进行改进的建议。

Storage Handlers

在 continue 之前,请阅读StorageHandlers以获得 HBase 集成所依赖的通用存储处理程序框架的概述。

Usage

存储处理程序是作为独立模块hive-hbase-handler-x.y.z.jar构建的,该模块必须与 HBase,Guava 和 ZooKeeper jar 一起在 HiveClient 端 auxpath 上可用。它还需要设置正确的配置属性才能连接到正确的 HBase 主站。有关如何设置 HBase 群集的信息,请参见HBase 文档

这是一个在源构建环境中使用 CLI 的示例,它以单节点 HBase 服务器为目标。 (请注意,jar 的位置和名称在 Hive 0.9.0 中已更改,因此对于较早的版本,需要进行一些更改.)

$HIVE_SRC/build/dist/bin/hive --auxpath $HIVE_SRC/build/dist/lib/hive-hbase-handler-0.9.0.jar,$HIVE_SRC/build/dist/lib/hbase-0.92.0.jar,$HIVE_SRC/build/dist/lib/zookeeper-3.3.4.jar,$HIVE_SRC/build/dist/lib/guava-r09.jar --hiveconf hbase.master=hbase.yoyodyne.com:60000

下面是一个示例,该示例针对的是分布式 HBase 群集,在该群集中,使用 3 位动物园 Management 员的法定人数选举 HBase 主服务器:

$HIVE_SRC/build/dist/bin/hive --auxpath $HIVE_SRC/build/dist/lib/hive-hbase-handler-0.9.0.jar,$HIVE_SRC/build/dist/lib/hbase-0.92.0.jar,$HIVE_SRC/build/dist/lib/zookeeper-3.3.4.jar,$HIVE_SRC/build/dist/lib/guava-r09.jar --hiveconf hbase.zookeeper.quorum=zk1.yoyodyne.com,zk2.yoyodyne.com,zk3.yoyodyne.com

该处理程序需要 Hadoop 0.20 或更高版本,并且仅已使用依赖版本 hadoop-0.20.x,hbase-0.92.0 和 zookeeper-3.3.4 进行了测试。如果您未使用 hbase-0.92.0,则需要使用与您的版本匹配的 HBase jar 来重建处理程序,并相应地更改上面的--auxpath。由于 HBase RPC 协议经常更改,因此无法使用匹配版本会导致误导连接失败,例如 MasterNotRunningException。

为了创建要由 HiveManagement 的新 HBase 表,请使用CREATE TABLE上的STORED BY子句:

CREATE TABLE hbase_table_1(key int, value string) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,cf1:val")
TBLPROPERTIES ("hbase.table.name" = "xyz", "hbase.mapred.output.outputtable" = "xyz");

hbase.columns.mapping属性是必需的,将在下一部分中进行说明。 hbase.table.name属性是可选的;它控制 HBase 已知的表名,并允许 Hive 表具有不同的名称。在此示例中,该表在 Hive 中被称为hbase_table_1,在 HBase 中被称为xyz。如果未指定,则 Hive 和 HBase 表名称将相同。 hbase.mapred.output.outputtable属性是可选的;如果您打算将数据插入表中,则需要此属性(hbase.mapreduce.TableOutputFormat使用该属性)

执行完上述命令后,您应该能够在 HBase Shell 中看到新的(空)表:

$ hbase shell
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Version: 0.20.3, r902334, Mon Jan 25 13:13:08 PST 2010
hbase(main):001:0> list
xyz                                                                                                           
1 row(s) in 0.0530 seconds
hbase(main):002:0> describe "xyz"
DESCRIPTION                                                             ENABLED                               
 {NAME => 'xyz', FAMILIES => [{NAME => 'cf1', COMPRESSION => 'NONE', VE true                                  
 RSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY =>                                       
  'false', BLOCKCACHE => 'true'}]}                                                                            
1 row(s) in 0.0220 seconds
hbase(main):003:0> scan "xyz"
ROW                          COLUMN+CELL                                                                      
0 row(s) in 0.0060 seconds

注意,即使在 Map 中指定了列名“ val”,在 HBase shell 的 DESCRIBE 输出中也只有列族名“ cf1”出现。这是因为在 HBase 中,表级元数据中仅列族(而非列)是已知的。列族中的列名称仅在每行级别显示。

以下是将数据从 Hive 移到 HBase 表中的方法(有关如何在 Hive 中首先创建示例表pokes的信息,请参见GettingStarted):

INSERT OVERWRITE TABLE hbase_table_1 SELECT * FROM pokes WHERE foo=98;

使用 HBase shell 验证数据是否实际加载:

hbase(main):009:0> scan "xyz"
ROW                          COLUMN+CELL                                                                      
 98                          column=cf1:val, timestamp=1267737987733, value=val_98                            
1 row(s) in 0.0110 seconds

然后通过 Hive 查询回来:

hive> select * from hbase_table_1;
Total MapReduce jobs = 1
Launching Job 1 out of 1
...
OK
98	val_98
Time taken: 4.582 seconds

由于 WAL 开销,插入大量数据可能会很慢;如果要禁用此功能,请确保您拥有 HIVE-1383(从 Hive 0.6 开始),然后在 INSERT 之前发出以下命令:

set hive.hbase.wal.enabled=false;

警告: 如果发生 HBase 故障,则禁用 WAL 可能会导致数据丢失,因此仅当您有其他可用的恢复策略时才使用此功能。

如果要授予 Hive 对现有 HBase 表的访问权限,请使用 CREATE EXTERNAL TABLE:

CREATE EXTERNAL TABLE hbase_table_2(key int, value string) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = "cf1:val")
TBLPROPERTIES("hbase.table.name" = "some_existing_table", "hbase.mapred.output.outputtable" = "some_existing_table");

同样,hbase.columns.mapping是必需的(并将针对现有 HBase 表的列族进行验证),而hbase.table.name是可选的。 hbase.mapred.output.outputtable是可选的。

Column Mapping

有两个SERDEPROPERTIES控制 HBase 列到 Hive 的 Map:

  • hbase.columns.mapping

  • hbase.table.default.storage.type:值可以为string(默认值)或binary,此选项仅在 Hive 0.9 及更高版本中可用,并且string行为是早期版本中唯一可用的选项

当前可用的列 Map 支持有些繁琐且受限:

  • 对于每个 Hive 列,表创建者必须在逗号分隔的hbase.columns.mapping字符串中指定一个对应的条目(因此,对于具有* n 列的 Hive 表,该字符串应具有 n *个条目);不应在条目之间使用空格,因为它们将作为列名的一部分被插入,这几乎肯定不是您想要的

  • Map 条目必须为:key:timestampcolumn-family-name:[column-name][#(binary|string)的形式(在 Hive 0.9.0中添加了用*#*分隔的类型说明,早期版本将所有内容解释为字符串)

  • 如果未给出类型说明,则将使用hbase.table.default.storage.type中的值

    • 有效值的任何前缀也都是有效的(即#b而不是#binary)

    • 如果将一列指定为binary,则相应的 HBase 单元中的字节应采用 HBase 的Bytes类产生的形式。

  • 必须有一个完整的:keyMap(可以 Map 到字符串或 struct 列–参见简单的组合键复杂的复合键)

  • (请注意,在 Hive 0.6 中的HIVE-1228之前,不支持:key,并且第一个 Hive 列隐式 Map 到键;从 Hive 0.6 开始,现在强烈建议您始终明确指定键;我们将不再支持隐式键将来 Map)

  • 如果没有给出列名,则 Hive 列将 Map 到相应 HBase 列族中的所有列,并且 Hive MAP 数据类型必须用于允许访问这些(可能是稀疏的)列

  • 从 HBase 1.1(HBASE-2828)开始,有一种使用特殊的:timestampMap 访问 HBase 时间戳属性的方法。它必须是biginttimestamp

  • 不必引用每个 HBase 列族,但是未 Map 的 HBase 列族将无法通过 Hive 表访问;可以将多个 Hive 表 Map 到同一个 HBase 表

接下来的几节提供了当前可能的各种列 Map 的详细示例。

多个列和系列

这是一个具有三个 Hive 列和两个 HBase 列族的示例,其中两个 Hive 列(value1value2)对应于一个列族(a,HBase 列名称为bc),另一个 Hive 列对应于自己的列族(d)中的单个列(e)。

CREATE TABLE hbase_table_1(key int, value1 string, value2 int, value3 int) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key,a:b,a:c,d:e"
);
INSERT OVERWRITE TABLE hbase_table_1 SELECT foo, bar, foo+1, foo+2 
FROM pokes WHERE foo=98 OR foo=100;

这是在 HBase 中的外观:

hbase(main):014:0> describe "hbase_table_1"
DESCRIPTION                                                             ENABLED                               
 {NAME => 'hbase_table_1', FAMILIES => [{NAME => 'a', COMPRESSION => 'N true                                  
 ONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_M                                       
 EMORY => 'false', BLOCKCACHE => 'true'}, {NAME => 'd', COMPRESSION =>                                        
 'NONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN                                       
 _MEMORY => 'false', BLOCKCACHE => 'true'}]}                                                                  
1 row(s) in 0.0170 seconds
hbase(main):015:0> scan "hbase_table_1"
ROW                          COLUMN+CELL                                                                      
 100                         column=a:b, timestamp=1267740457648, value=val_100                               
 100                         column=a:c, timestamp=1267740457648, value=101                                   
 100                         column=d:e, timestamp=1267740457648, value=102                                   
 98                          column=a:b, timestamp=1267740457648, value=val_98                                
 98                          column=a:c, timestamp=1267740457648, value=99                                    
 98                          column=d:e, timestamp=1267740457648, value=100                                   
2 row(s) in 0.0240 seconds

当查询回 Hive 时:

hive> select * from hbase_table_1;
Total MapReduce jobs = 1
Launching Job 1 out of 1
...
OK
100	val_100	101	102
98	val_98	99	100
Time taken: 4.054 seconds

Hive MAP 到 HBase 列系列

以下是 Hive MAP 数据类型可用于访问整个列族的方式。每行可以具有一组不同的列,其中列名对应于 Map 键,列值对应于 Map 值。

CREATE TABLE hbase_table_1(value map<string,int>, row_key int) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = "cf:,:key"
);
INSERT OVERWRITE TABLE hbase_table_1 SELECT map(bar, foo), foo FROM pokes 
WHERE foo=98 OR foo=100;

(此示例还演示了使用第一个列以外的 Hive 列作为 HBase 行键.)

这是在 HBase 中的外观(在不同的行中具有不同的列名):

hbase(main):012:0> scan "hbase_table_1"
ROW                          COLUMN+CELL                                                                      
 100                         column=cf:val_100, timestamp=1267739509194, value=100                            
 98                          column=cf:val_98, timestamp=1267739509194, value=98                              
2 row(s) in 0.0080 seconds

当查询回 Hive 时:

hive> select * from hbase_table_1;
Total MapReduce jobs = 1
Launching Job 1 out of 1
...
OK
{"val_100":100}	100
{"val_98":98}	98
Time taken: 3.808 seconds

请注意,MAP 的键必须具有数据类型字符串,因为它用于命名 HBase 列,因此以下表定义将失败:

CREATE TABLE hbase_table_1(key int, value map<int,int>) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key,cf:"
);
FAILED: Error in metadata: java.lang.RuntimeException: MetaException(message:org.apache.hadoop.hive.serde2.SerDeException org.apache.hadoop.hive.hbase.HBaseSerDe: hbase column family 'cf:' should be mapped to map<string,?> but is mapped to map<int,int>)

Hive MAP 到 HBase 列前缀

另请注意,以Hive 0.12开头,通配符还可用于检索列。例如,如果要检索 HBase 中所有以前缀“ col_prefix”开头的列,则应执行以下查询:

CREATE TABLE hbase_table_1(value map<string,int>, row_key int) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = "cf:col_prefix.*,:key"
);

尽管有相同的限制。也就是说,Map 的键应该是一个字符串,因为它 Map 到 HBase 列名,并且值可以是您要检索的值的类型。另一个限制是,给定前缀下的所有值都应为同一类型。也就是说,它们都应为“ int”类型或“ string”类型,依此类推。

隐藏列前缀

Hive 1.3.0开始,可以在选择查询结果中隐藏列前缀。有一个 SerDe 布尔属性 hbase.columns.mapping.prefix.hide(默认为 false),它定义是否在 HiveMap 的键中隐藏该前缀:

CREATE TABLE hbase_table_1(tags map<string,int>, row_key string) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = "cf:tag_.*,:key",
"hbase.columns.mapping.prefix.hide" = "true"
);

然后,“标签”(select tags from hbase_table_1)列的值将为:

"x" : 1

instead of:

"tag_x" : 1

非法:Hive 原始到 HBase 列系列

如下表定义是非法的,因为
Map 到整个列族的 Hive 列必须具有 MAP 类型:

CREATE TABLE hbase_table_1(key int, value string) 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key,cf:"
);
FAILED: Error in metadata: java.lang.RuntimeException: MetaException(message:org.apache.hadoop.hive.serde2.SerDeException org.apache.hadoop.hive.hbase.HBaseSerDe: hbase column family 'cf:' should be mapped to map<string,?> but is mapped to string)

具有二进制列的示例

依靠默认值hbase.table.default.storage.type

CREATE TABLE hbase_table_1 (key int, value string, foobar double)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key#b,cf:val,cf:foo#b"
);

指定hbase.table.default.storage.type

CREATE TABLE hbase_table_1 (key int, value string, foobar double)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key,cf:val#s,cf:foo",
"hbase.table.default.storage.type" = "binary"
);

简单的复合行键

Version information

通过将 HBase 行键 Map 到 Hive 结构,并使用 ROW FORMAT DELIMITED ... COLLECTION ITTES TERMINATED BY,Hive 可以将定界的组合键读写到 HBase。例:

-- Create a table with a composite row key consisting of two string fields, delimited by '~'
CREATE EXTERNAL TABLE delimited_example(key struct<f1:string, f2:string>, value string)
ROW FORMAT DELIMITED
COLLECTION ITEMS TERMINATED BY '~'
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ( 
  'hbase.columns.mapping'=':key,f:c1');

复杂的复合行键和 HBaseKeyFactory

Note

从带有HIVE-6411的 Hive 0.14.0 开始(0.13.0 还支持复杂的复合键,但是使用了不同的界面–有关该界面,请参见HIVE-2599)

对于更复杂的用例,Hive 允许用户指定一个 HBaseKeyFactory,它定义键到 Hive 结构中字段的 Map。可以使用 SERDEPROPERTIES 选项中的“ hbase.composite.key.factory”属性进行配置:

-- Parse a row key with 3 fixed width fields each of width 10
-- Example taken from: https://svn.apache.org/repos/asf/hive/trunk/hbase-handler/src/test/queries/positive/hbase_custom_key2.q
CREATE TABLE hbase_ck_4(key struct<col1:string,col2:string,col3:string>, value string)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
    "hbase.table.name" = "hbase_custom2",
    "hbase.mapred.output.outputtable" = "hbase_custom2",
    "hbase.columns.mapping" = ":key,cf:string",
    "hbase.composite.key.factory"="org.apache.hadoop.hive.hbase.SampleHBaseKeyFactory2");

“ hbase.composite.key.factory”应该是实现HBaseKeyFactory的类的标准类名。请参见SampleHBaseKeyFactory2,以获取同一包中的固定长度示例。该类必须在您的 Classpath 中,才能使以上示例正常工作。待办事项:将它们放置在容易接近的地方;他们目前仅在测试代码中。

HBase 列中存储的 Avro 数据

Note

自 Hive 0.14.0 起HIVE-6147

Hive 0.14.0 及更高版本支持通过将 Hro 列显示为 Hive 的结构来在 HBase 列中存储和查询 Avro 对象。这使 Hive 可以对可以深度构建的 HBase 数据执行临时分析。在 0.14.0 之前,HBase Hive 集成仅支持查询列中的原始数据类型。

示例 HiveQL 语句,其中test_col_fam是列族,而test_col是列名:

CREATE EXTERNAL TABLE test_hbase_avro
ROW FORMAT SERDE 'org.apache.hadoop.hive.hbase.HBaseSerDe' 
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' 
WITH SERDEPROPERTIES (
	"hbase.columns.mapping" = ":key,test_col_fam:test_col", 
	"test_col_fam.test_col.serialization.type" = "avro",
	"test_col_fam.test_col.avro.schema.url" = "hdfs://testcluster/tmp/schema.avsc")
TBLPROPERTIES (
    "hbase.table.name" = "hbase_avro_table",
    "hbase.mapred.output.outputtable" = "hbase_avro_table",
    "hbase.struct.autogenerate"="true");

要注意的重要属性是以下三个:

"test_col_fam.test_col.serialization.type" = "avro"

此属性告诉 Hive,给定列族下的给定列是 Avro 列,因此 Hive 需要对其进行反序列化。

"test_col_fam.test_col.avro.schema.url" = "hdfs://testcluster/tmp/schema.avsc"

使用此属性,您可以为将用于反序列化的列指定阅读器架构的位置。可以在此处提到的 HDFS 上使用,也可以使用"test_col_fam.test_col.avro.schema.literal"属性内联提供。如果您有一个用于存储此架构的自定义存储,则可以编写AvroSchemaRetriever的自定义实现,并使用"test_col_fam.test_col.avro.schema.retriever"等属性使用"avro.schema.retriever property"将其插入。您需要确保具有此自定义类的 jar 在 HiveClasspath 上。有关用法的讨论以及指向其他资源的链接,请参见HIVE-6147

"hbase.struct.autogenerate" = "true"

通过指定此属性,Hive 可以使用提供的模式自动推断列和类型。这样,您就可以避免为 Avro 模式手动创建列和类型,这些列和类型可能很复杂并且嵌套很深。

Put Timestamps

Version information

从 Hive 0.9.0开始

如果使用 Hive 插入 HBase 表中,则会添加 HBase 默认时间戳,通常是当前时间戳。可以使用SERDEPROPERTIES选项hbase.put.timestamp(必须是有效的时间戳记或-1)在每个表的基础上重写,以重新启用默认策略。

Key Uniqueness

HBase 表和 Hive 表之间的细微差别是 HBase 表具有唯一键,而 Hive 表则没有。将具有相同键的多行插入到 HBase 中时,只会存储其中的一行(选择是任意的,因此不要依赖 HBase 来选择正确的行)。与之相反,Hive 乐于存储具有相同键和不同值的多行。

例如,pokes 表包含具有重复键的行。如果将其复制到另一个 Hive 表中,则将保留重复项:

CREATE TABLE pokes2(foo INT, bar STRING);
INSERT OVERWRITE TABLE pokes2 SELECT * FROM pokes;
-- this will return 3
SELECT COUNT(1) FROM POKES WHERE foo=498;
-- this will also return 3
SELECT COUNT(1) FROM pokes2 WHERE foo=498;

但是在 HBase 中,重复项被静默消除:

CREATE TABLE pokes3(foo INT, bar STRING)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key,cf:bar"
);
INSERT OVERWRITE TABLE pokes3 SELECT * FROM pokes;
-- this will return 1 instead of 3
SELECT COUNT(1) FROM pokes3 WHERE foo=498;

Overwrite

HBase 表和其他 Hive 表之间要注意的另一个区别是,使用 INSERT OVERWRITE 时,不会从表中删除现有行。但是,如果现有行具有与新行匹配的键,则会被覆盖。

Potential Followups

Hive/HBase 集成在许多领域肯定可以使用更多的爱:

  • 更灵活的列 Map(HIVE-806,HIVE-1245)

  • 如果没有给出 Map 说明,则使用默认列 Map

  • 过滤器下推和索引编制(请参阅FilterPushdownDevIndexDev)

  • 公开 timestamp 属性,可能还支持将其视为分区键

  • 允许每表 hbase.master 配置

  • 运行事件探查器并使列 Map 中的每行开销最小化

  • 用户定义的例程,用于通过 HBaseClient 端 API(HIVE-758 和 HIVE-791)进行查找和数据加载

  • 日志记录非常嘈杂,有很多虚 Pseudoexception。调查这些并解决其原因或抑制它们

Build

存储处理程序的代码位于hive/trunk/hbase-handler下。

HBase 和 Zookeeper 依赖关系是通过 ivy 获取的。

Tests

hbase-handler/src/test/org/apache/hadoop/hive/hbase下提供了类单元测试。

积极的 QL 测试在hbase-handler/src/test/queries下。它们使用 HBase Zookeeper 微型集群在过程中托管灯具表,因此无需独立安装 HBase 即可运行它们。为避免端口冲突导致的故障,请勿尝试在运行实际 HBase 主服务器或 Zookeeper 的同一台计算机上运行这些测试。

可以通过 ant 这样执行 ant 来执行 QL 测试:

ant test -Dtestcase=TestHBaseCliDriver -Dqfile=hbase_queries.q

Eclipse 启动模板仍有待定义。

  • 有关如何将数据从 Hive 批量加载到 HBase 的信息,请参见HBaseBulkLoad

  • 有关在 HBase 顶部添加类似 SQL 的查询语言支持的另一个项目,请参见HBQL(与 Hive 不相关)。

Acknowledgements

  • 此功能的主要贡献者是 Samuel Guo,他在补丁的早期草案中完成了大部分开发工作

未解决问题(JIRA)

||
||
|T|Key|Summary|Assignee|Reporter|P|Status|Resolution|Created|Updated|Due|

Loading...

Refresh