hive与hbase集成

hive与hbase集成

这篇文章最初是基于介绍HIVE-705。这个功能允许Hive QL命令访问HBase表,进行读(select)、写(insert)操作。它甚至可以基于join、union操作对hbase表和hive原生的表进行混合访问。

这个功能还在不断的完善中,欢迎提出建议。

存储handler

在开始介绍之前,首先请阅读StorageHandlers,对存储处理程序的框架有个初步的认识,可以帮助读者理解HBase集成。

使用方法

这个存储处理程序被编译成一个独立的模块, hive-hbase-handler-x.y.z.jar,必须与guava、zookeeper的jar包被hive的客户端程序识别到(通过--auxpath指定)。也需要正确的配置hbase master的ip地址,保证能够与hbase连接。启动HBase集群的方法,请见the HBase documentation。

这里有个例子是使用源代码编译环境的命令行,连接到一个单结点的HBase server。(注意,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结点的zookeeper集群来确定HBase的master结点:

$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包相对应的handler处理程序,并且修改上文中提到的相应的--auxpath命令行参数。使用不对应的hbase jar包编译,将会导致连接错误异常,比如 MasterNotRunningException,这是因为hbase的RPC协议经常随版本更新发生变化。

为了创建一个新的可供hive管理的HBase表,需要使用“ STORED BY clause on CREATE TABLE”命令:

[python]  view plain copy
  1. CREATE TABLE hbase_table_1(key int, value string)   
  2. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'  
  3. WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,cf1:val")  
  4. TBLPROPERTIES ("hbase.table.name" = "xyz");  

这个hbase.columns.mapping属性是必需的,将会被下一段的所解释。hbase.table.name属性是可选的,它控制了HBase中创建的表的名字,并且允许其与hive表使用不同的名字。在这个例子中欧,在hive中的表名是hbase_table_1,在HBase中,则被命名为xyz。如果没有特别标名,hive与hbase表将会使用相同的名字。

在以上命令执行之后,我们就能通过HBase的shell看到一个新的空表:

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

注意,即使在映射中配置了“val”这个列名,但是在hbase shell输出的描述中,只能看到列族(column family)的名字“cf1”。这是因为在HBase中,只有列族(而不是列)的名字会被保存在表级的元数据中;列族中的列名只能保存在底层的列数据中。

这里将介绍如何将数据从Hive中转移到HBase的表中(如果创建一个示例表pokes,可以参考GettingStarted):

[python]  view plain copy
  1. INSERT OVERWRITE TABLE hbase_table_1 SELECT * FROM pokes WHERE foo=98;  
使用HBase shell来验证一下数据是否被加载:

[python]  view plain copy
  1. hbase(main):009:0> scan "xyz"  
  2. ROW                          COLUMN+CELL                                                                        
  3.  98                          column=cf1:val, timestamp=1267737987733, value=val_98                              
  4. 1 row(s) in 0.0110 seconds  
通过hive进行检索的结果是:

[python]  view plain copy
  1. hive> select * from hbase_table_1;  
  2. Total MapReduce jobs = 1  
  3. Launching Job 1 out of 1  
  4. ...  
  5. OK  
  6. 98  val_98  
  7. Time taken: 4.582 seconds  
Inserting large amounts of data may be slow due to WAL overhead; if you would like to disable this, make sure you have HIVE-1383 (as of Hive 0.6), and then issue this command before the INSERT:

插入大量的数据将会因为WAL机制导致速度缓慢;如果想禁用该功能,要确保已经打过HIVE-1383(针对hive 0.6),然后在插入数据之前,执行以下命令:

[python]  view plain copy
  1. set hive.hbase.wal.enabled=false;  

警告: 禁用WAL功能将会导致HBase在异常出错时丢失数据,所以只能在还有其他恢复手段的时候使用这个功能。

如果你想让hive访问HBase上已经存在的表,可以使用创建外表功能“CREATE EXTERNAL TABLE”:

[python]  view plain copy
  1. CREATE EXTERNAL TABLE hbase_table_2(key int, value string)   
  2. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'  
  3. WITH SERDEPROPERTIES ("hbase.columns.mapping" = "cf1:val")  
  4. TBLPROPERTIES("hbase.table.name" = "some_existing_table");  

hbase.columns.mapping仍然是必需的,而且要保证它与HBase表上现有的列族相对应。hbase.table.name是可选项。

列的映射

这里有两种SERDEPROPERTIES,将会对HBase列映射到hive产生影响:

  • hbase.columns.mapping
  • hbase.table.default.storage.tepy:  它可以被设置为“string”(默认值)或者binary,这个选项只在hive0.9版本以后才可用,因为之前的版本只支持string。

当前可用的列映射配置方法多少有些繁琐:

  • 对于每个hive列,表的创建者必须在hbase.columns.mapping字符串中,指定一系列相对应逗号分隔的映射项(有多少个hive列,就必须有多少个映射项与之相对于),注意空格不能用来表示entry,因为空格会被解释成列名的一部分。
  • 一个映射项必须为如下的形式:key或者列族名:[列名][#(二进制|字符串) (通过#的方式指定该映射项在HBase中以二进制数的形式保存,在hive 0.9.0版本之前,没有此功能)

    • 如果没有特别指定,则使用hbase.table.default.storage.type
    • Any prefixes of the valid values are valid too (i.e. #b instead of #binary)
    • 如果你指定一个列为二进制,则在HBase相应的单元格中以HBase的bytes类来保存相应的数值

  • 必须指定一个:key映射,当前还不支持复合主键
  • (注意在0.6版本的HIVE-1228之前,:key还不被支持,第一个hive列会隐式的被作为主键(rowkey);在Hive 0.6版本以后,强烈的推荐用户明确的指定作为rowkey的列;未来有可能会不再支持隐式主键的方式)
  • if no column-name is given, then the Hive column will map to all columns in the corresponding HBase column family, and the Hive MAP datatype must be used to allow access to these (possibly sparse) columns
  • 当前还没有方法能够读取HBase中的时间戳属性,查询中只能读取最新时间戳对应的数据
  • 因为HBase没有在列中给出数据类型的信息,所以序列化/反序列化模块先将其转换为字符串再存储到HBase中;现在还没有方法对每个列加入一个用户自定的序列化/反序列化模块。
  • 并不需要引用HBase中的每个列族,但是这会导致Hive表无法访问到HBase中的这个列;可以将多个Hive表映射到一个HBase表中

下面几段将会通过一个具体的例子来介绍当前可以使用的几种映射方式:

多个列和列族

这里有个例子是三个hive列映射到两个HBase的列族,两个Hive列(value1、value2)对应一个列族(a,其中两个HBase列名为b、c),另一个Hive列则对应一个列族(d)中的单独的列(e)

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

在hive中查看表结构,结果如下所示:

[plain]  view plain copy
  1. hbase(main):014:0> describe "hbase_table_1"  
  2. DESCRIPTION                                                             ENABLED                                 
  3.  {NAME => 'hbase_table_1', FAMILIES => [{NAME => 'a', COMPRESSION => 'N true                                    
  4.  ONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN_M                                         
  5.  EMORY => 'false', BLOCKCACHE => 'true'}, {NAME => 'd', COMPRESSION =>                                          
  6.  'NONE', VERSIONS => '3', TTL => '2147483647', BLOCKSIZE => '65536', IN                                         
  7.  _MEMORY => 'false', BLOCKCACHE => 'true'}]}                                                                    
  8. 1 row(s) in 0.0170 seconds  
  9. hbase(main):015:0> scan "hbase_table_1"  
  10. ROW                          COLUMN+CELL                                                                        
  11.  100                         column=a:b, timestamp=1267740457648, value=val_100                                 
  12.  100                         column=a:c, timestamp=1267740457648, value=101                                     
  13.  100                         column=d:e, timestamp=1267740457648, value=102                                     
  14.  98                          column=a:b, timestamp=1267740457648, value=val_98                                  
  15.  98                          column=a:c, timestamp=1267740457648, value=99                                      
  16.  98                          column=d:e, timestamp=1267740457648, value=100                                     
  17. 2 row(s) in 0.0240 seconds  

在hive中检索的结果为:

[plain]  view plain copy
  1. hive> select * from hbase_table_1;  
  2. Total MapReduce jobs = 1  
  3. Launching Job 1 out of 1  
  4. ...  
  5. OK  
  6. 100 val_100 101 102  
  7. 98  val_98  99  100  
  8. Time taken: 4.054 seconds  

Hive使用MAP类型映射整个列族

这里,举一个Hive使用MAP类型来读取这个column family的例子。每行包含不同的列,列的命名根据map中的key,列的值为map中的value:

[plain]  view plain copy
  1. CREATE TABLE hbase_table_1(value map<string,int>, row_key int)   
  2. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'  
  3. WITH SERDEPROPERTIES (  
  4. "hbase.columns.mapping" = "cf:,:key"  
  5. );  
  6. INSERT OVERWRITE TABLE hbase_table_1 SELECT map(bar, foo), foo FROM pokes   
  7. WHERE foo=98 OR foo=100;  
这个例子同时也验证了hive使用的第一个列不一定是rowkey所在的列

这个表在HBase中看起来是这样的:

[plain]  view plain copy
  1. hbase(main):012:0> scan "hbase_table_1"  
  2. ROW                          COLUMN+CELL                                                                        
  3.  100                         column=cf:val_100, timestamp=1267739509194, value=100                              
  4.  98                          column=cf:val_98, timestamp=1267739509194, value=98                                
  5. 2 row(s) in 0.0080 seconds  

在hive中进行检索,结果如下:

[plain]  view plain copy
  1. hive> select * from hbase_table_1;  
  2. Total MapReduce jobs = 1  
  3. Launching Job 1 out of 1  
  4. ...  
  5. OK  
  6. {"val_100":100} 100  
  7. {"val_98":98}   98  
  8. Time taken: 3.808 seconds  
注意,MAP类型中key必须是字符串,因为它将用来为HBase列命名,所以如下的表定义方式将会出错:

[plain]  view plain copy
  1. CREATE TABLE hbase_table_1(key int, value map<int,int>)   
  2. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'  
  3. WITH SERDEPROPERTIES (  
  4. "hbase.columns.mapping" = ":key,cf:"  
  5. );  
  6. 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>)  

违反规则的映射方式:

如下的表定义方式不合乎规则,因为只有MAP方式可以把一个hive列映射到整个列族:

[plain]  view plain copy
  1. CREATE TABLE hbase_table_1(key int, value string)   
  2. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'  
  3. WITH SERDEPROPERTIES (  
  4. "hbase.columns.mapping" = ":key,cf:"  
  5. );  
  6. 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:

[plain]  view plain copy
  1. CREATE TABLE hbase_table_1 (key int, value string, foobar double)  
  2. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'  
  3. WITH SERDEPROPERTIES (  
  4. "hbase.columns.mapping" = ":key#b,cf:val,cf:foo#b"  
  5. );  
特别指定hbase.table.default.storage.type为二进制:

[plain]  view plain copy
  1. CREATE TABLE hbase_table_1 (key int, value string, foobar double)  
  2. STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'  
  3. WITH SERDEPROPERTIES (  
  4. "hbase.columns.mapping" = ":key,cf:val#s,cf:foo",  
  5. "hbase.table.default.storage.type" = "binary"  
  6. );  


你可能感兴趣的:(hive与hbase集成)