Avro 数据存储在 HBase 列中

版本信息:

Avro 数据存储在 HBase 列中 从 Hive 0.9.0 开始,HBase 集成至少需要 HBase 0.92, 早期版本的 Hive 使用 HBase 0.89/0.90。

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

介绍

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

用法

存储处理程序构建为独立模块 hive-hbase-handler-x.y.z.jar,它必须在 Hive 客户端辅助路径以及 HBase、Guava 和 ZooKeeper jar 上可用。它还需要设置正确的配置属性才能连接到正确的 HBase 主服务器。

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

    $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 主节点:

    $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 。不使用匹配版本会导致误导性的连接失败,例如 MasterNotRunningException,因为 HBase RPC 协议经常变化。

    为了创建一个由 Hive 管理的新 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
    
  • 请注意,即使在映射中指定了列名“val”,HBase shell 的 DESCRIBE 输出中也只出现列族名称“cf1”。这是因为在 HBase 中,表级元数据中只有列族(而不是列)是已知的;列族中的列名仅出现在每行级别。

    以下是如何将数据从 Hive 移动到 HBase 表中:

    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 是可选的。

列映射

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

  • hbase.columns.mapping
  • hbase.table.default.storage.type: 可以具有字符串(默认值)或二进制值,此选项仅从 Hive 0.9 开始可用,并且字符串行为是早期版本中唯一可用的

当前可用的列映射支持有些繁琐和限制:

  • 对于每个 Hive 列,表创建者必须在以逗号分隔的 hbase.columns.mapping 字符串中指定相应的条目(因此对于具有 n 列的 Hive 表,该字符串应具有 n 个条目);不应在条目之间使用空格,因为它们将作为列名的一部分进行解释,这几乎肯定不是您想要的。
  • 映射条目必须是 :key, :timestamp 或 column-family-name:[column-name][#(binary|string)(以# 分隔的类型规范是在 Hive 0.9.0 中添加的,早期版本将所有内容解释为字符串)
    • 如果没有指定类型,将使用 hbase.table.default.storage.type 中的值
    • 有效值的任何前缀也是有效的(即#b 而不是#binary)
    • 如果您将列指定为二进制,则相应 HBase 单元格中的字节应采用 HBase 的 Bytes 类产生的形式。
  • 必须只有一个 :key 映射(这可以映射到字符串或结构列 - 请参阅简单复合键和复杂复合键)
  • (请注意,在 Hive 0.6 中的 HIVE-1228 之前,不支持 :key,并且第一个 Hive 列隐式映射到键;从 Hive 0.6 开始,现在强烈建议您始终明确指定密钥;我们将在未来放弃对隐式键映射的支持)
  • 如果没有给出 column-name,则 Hive 列将映射到相应 HBase 列族中的所有列,并且必须使用 Hive MAP 数据类型来允许访问这些(可能是稀疏的)列。
  • 从 HBase 1.1 (HBASE-2828) 开始,有一种方法可以使用特殊的 :timestamp 映射来访问 HBase 时间戳属性。它需要是 bigint 或时间戳。
  • 没有必要引用每个 HBase 列族,但那些没有映射的列族将无法通过 Hive 表访问;可以将多个 Hive 表映射到同一个 HBase 表 接下来的几节提供了当前可能的列映射类型的详细示例。

多个列和列簇

  • 这是一个包含三个 Hive 列和两个 HBase 列族的示例,两个 Hive 列(value1 和 value2)对应于其中一个列族(a,具有 HBase 列名 b 和 c),另一个 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 数据类型访问整个列族。每行可以有一组不同的列,其中列名对应于映射键,列值对应于映射值。
    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;
    
    • 这是在 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的key必须有数据类型string,因为它是用来命名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"
    );
    
  • 但同样的限制也适用。也就是说,映射的键应该是一个字符串,因为它映射到 HBase 列名,并且值可以是您正在检索的值的类型。另一个限制是给定前缀下的所有值都应该是相同的类型。也就是说,它们都应该是“int”类型或“string”类型等等。
隐藏列前缀
  • 从 Hive 1.3.0 开始,可以在选择查询结果中隐藏列前缀。有 SerDe 布尔属性 hbase.columns.mapping.prefix.hide(默认为 false),它定义前缀是否应该隐藏在 Hive 映射的键中:
    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"
    );
    
  • 然后“标签”列的值(从 hbase_table_1 中选择标签)将是:
    "x" : 1
    instead of:
    
    "tag_x" : 1
    
非法:Hive 原语到 HBase 列族
  • 如下表定义是非法的,因为 映射到整个列族的 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"
    );
    
简单的复合行键(As of Hive 0.13.0)
  • 通过将 HBase 行键映射到 Hive 结构,并使用 ROW FORMAT DELIMITED…COLLECTION ITEMS 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

从带有 HIVE-6411 的 Hive 0.14.0 开始(0.13.0 也支持复杂的复合键

  • 对于更复杂的用例,Hive 允许用户指定 HBaseKeyFactory,它定义键到 Hive 结构中字段的映射。这可以使用 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。此类必须在您的类路径上才能使上述示例正常工作。待办事项:将它们放置在可访问的地方;他们目前只在测试代码中。
Avro 数据存储在 HBase 列中(As of Hive 0.14.0)
  • Hive 0.14.0 以后支持在 HBase 列中存储和查询 Avro 对象,方法是使它们作为 Hive 的结构可见。这允许 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 的自定义实现并使用“avro.schema.retriever 属性”使用“test_col_fam.test_col.avro.schema.retriever”之类的属性将其插入。您需要确保具有此自定义类的 jar 位于 Hive 类路径上。
      "hbase.struct.autogenerate" = "true"
      
    • 指定此属性可让 Hive 使用提供的架构自动推断列和类型。这使您可以避免为 Avro 架构手动创建列和类型,这可能很复杂且嵌套很深。
Logo

一站式虚拟内容创作平台,激发创意,赋能创作,进入R空间,遇见同道,让优质作品闪耀发光。​

更多推荐