Impala 查询HBase表


使用Impala查询HBase表

您可以使用Impala查询HBase表。这对于通过SQL访问任何现有的HBase表并对其执行分析非常有用。HDFS和Kudu表比HBase更适合分析工作负载,并提供卓越的性能。Kudu支持少量行的高效插入、更新和删除,并且可以在大多数面向分析的用例中替换HBase。

从一个Impala用户的角度来看,来自RDBMS背景,HBase是一种键值存储,其中值由多个字段组成。键映射到Impala表中的一列,值的各个字段映射到Impala表中的其他列。

将HBase与Impala结合使用的概述

当您将Impala与HBase结合使用时:

  • 您使用Hiveshell在Impala端创建表,因为ImpalaCREATETABLE语句当前不支持这些表所需的自定义SerDes和一些其他语法:
    • 您可以使用STOREDBY'org.apache.hadoop.hive.hbase.HBaseStorageHandler'HiveCREATETABLE语句上的子句将其指定为HBase表。
    • 您可以使用Hive语句上的子句将这些专门创建的表映射到HBase中存在的相应表。TBLPROPERTIES("hbase.table.name"="table_name_in_hbase")CREATETABLE
  • 您将与HBase行键对应的列定义为带有#string关键字的字符串,或将其映射到STRING列。
  • 由于Impala和Hive共享同一个Metastore数据库,因此在Hive中创建表后,您可以通过Impala查询或插入其中。(通过Hive创建新表后,INVALIDATEMETADATA在impala-shell中发出语句,让Impala知道新表。)
  • 您对Impala表发出查询。对于高效查询,WHERE通过测试与HBase行键对应的Impala列,在可行的情况下使用该子句查找单个键值或一系列键值。避免执行全表扫描的查询,这对常规Impala表有效,但在HBase中效率低下。

要使用来自Impalaimpala的HBase表,请使用GRANTHBaseshell中的命令确保用户对HBase表具有读/写权限。

配置HBase以与Impala一起使用

HBase与Impala一起开箱即用。一起使用这两个组件不需要强制配置。

为了避免在Impala启动期间或INVALIDATEMETADATA语句之后HBase不可用时出现延迟,请在/etc/impala/conf/hbase-site.xml中设置类似于以下内容的超时值:

<property>
  <name>hbase.client.retries.number</name>
  <value>3</value>
</property>
<property>
  <name>hbase.rpc.timeout</name>
  <value>3000</value>
</property>

HBase列支持的数据类型

要了解Impala列数据类型如何映射到HBase中的字段,您应该先了解一些HBase的背景知识。您可以通过CREATETABLE在Hiveshell中运行语句来设置映射。

HBase作为一种“位桶”工作,因为HBase不强制对键或值字段进行任何输入。所有类型强制都在Impala端完成。

为了对HBase表进行Impala查询的最佳性能,大多数查询将在WHERE子句中与对应于HBase行键的列执行比较。通过Hiveshell创建表时,使用STRING与HBase行键对应的列的数据类型。因帕拉可以翻译谓词(通过运营商,如=<以及BETWEEN针对此列到HBase的快速查找),但这种优化(“谓词下推”),只有当该列被定义为工作STRING

从Impala1.1开始,Impala还支持读取和写入CREATETABLE使用二进制数据类型在Hive语句中定义的列,在Hive表定义中使用#binary关键字表示,通常缩写为#b。将数字列定义为二进制可以减少HBase表中的整体数据量。您仍应将与HBase行键对应的列定义为aSTRING,以允许使用这些列进行快速查找。

Impala-HBase集成的性能注意事项

要了解针对存储在HBase中的数据的SQL查询的性能特征,您应该首先了解一些有关HBase如何与面向SQL的系统交互的背景知识。

Impala通过JavaNativeInterface(JNI)使用HBase客户端API来查询存储在HBase中的数据。此查询不直接读取HFile。额外的通信开销使得选择将哪些数据存储在HBase或HDFS中并构建可以高效检索HBase数据的高效查询变得非常重要:

  • 将HBase表用于返回单行或小范围行的查询,而不是对整个表执行全表扫描的查询。(如果查询具有HBase表并且没有WHERE引用该表的子句,则强烈表明它是对HBase表的低效查询。)
  • HBase可以为存储小维度表提供可接受的性能,其中表足够小以至于对每个查询执行全表扫描足够有效。但是,Kudu几乎总是存储维度表的绝佳替代方案。HDFS表也适用于不需要支持更新查询、删除查询或行数较少的插入查询的维度表。

查询谓词作为开始和停止键应用于行键,从而限制特定查找的范围。如果行键未映射到字符串列,则排序通常不正确并且比较操作不起作用。例如,如果行键未映射到字符串列,则无法完成对大于(>)或小于(<)的评估。

可以将非键列上的谓词发送到HBase以扫描为SingleColumnValueFilters,从而提供一些性能提升。在这种情况下,与使用Impala应用相同谓词的情况相比,HBase返回的行数更少。虽然有一些改进,但在使用开始和停止行时效果并不好。这是因为HBase必须检查的行数不受使用开始和停止行时的限制。只要行键谓词只适用于单行,HBase就会定位并返回该行。相反,如果使用非键谓词,即使它只适用于单行,HBase仍然必须扫描整个表才能找到正确的结果。

解释HBase查询的EXPLAIN输出

例如,这里有一些针对以下Impala表的查询,该表映射到一个HBase表。这些示例显示了EXPLAIN语句输出的摘录,演示了要查找哪些内容来指示对HBase表的高效或低效查询。

第一列(cust_id)被指定为CREATEEXTERNALTABLE语句中的关键列;为了性能,将此列声明为STRING.其他列(例如BIRTH_YEARandNEVER_LOGGED_ON)也声明为STRING,而不是它们的“自然”类型INTorBOOLEAN,因为Impala可以在HBase表中更有效地优化这些类型。为了便于比较,我们离开一列YEAR_REGISTERED,为INT表明对本专栏过滤是低效的。

describe hbase_table;
Query: describe hbase_table
+-----------------------+--------+---------+
| name                  | type   | comment |
+-----------------------+--------+---------+
| cust_id               | string |         |
| birth_year            | string |         |
| never_logged_on       | string |         |
| private_email_address | string |         |
| year_registered       | int    |         |
+-----------------------+--------+---------+

性能的最佳情况涉及在定义为行键的列上使用相等比较进行单行查找:

explain select count(*) from hbase_table where cust_id = 'some_user@example.com';
+------------------------------------------------------------------------------------+
| Explain String                                                                     |
+------------------------------------------------------------------------------------+
| Estimated Per-Host Requirements: Memory=1.01GB VCores=1                            |
| WARNING: The following tables are missing relevant table and/or column statistics. |
| hbase.hbase_table                                                                  |
|                                                                                    |
| 03:AGGREGATE [MERGE FINALIZE]                                                      |
| |  output: sum(count(*))                                                           |
| |                                                                                  |
| 02:EXCHANGE [PARTITION=UNPARTITIONED]                                              |
| |                                                                                  |
| 01:AGGREGATE                                                                       |
| |  output: count(*)                                                                |
| |                                                                                  |
| 00:SCAN HBASE [hbase.hbase_table]                                                  |
|    start key: some_user@example.com                                                |
|    stop key: some_user@example.com\0                                               |
+------------------------------------------------------------------------------------+

另一种类型的高效查询涉及行键列的范围查找,使用SQL运算符,例如大于(或等于)、小于(或等于)或BETWEEN。此示例还包括对非键列的相等性测试;因为该列是aSTRING,所以Impala可以让HBase执行该测试,由输出中的hbasefilters:行指示EXPLAIN。在HBase中进行过滤比将所有数据传输到Impala并在Impala端进行过滤更有效。

explain select count(*) from hbase_table where cust_id between 'a' and 'b'
  and never_logged_on = 'true';
+------------------------------------------------------------------------------------+
| Explain String                                                                     |
+------------------------------------------------------------------------------------+
...

| 01:AGGREGATE                                                                       |
| |  output: count(*)                                                                |
| |                                                                                  |
| 00:SCAN HBASE [hbase.hbase_table]                                                  |
|    start key: a                                                                    |
|    stop key: b\0                                                                   |
|    hbase filters: cols:never_logged_on EQUAL 'true'                                |
+------------------------------------------------------------------------------------+

如果Impala必须评估任何谓词,则查询效率较低,因为Impala必须扫描整个HBase表。Impala只能将声明为的列的谓词下推到HBaseSTRING。此示例测试声明为的列INT,输出中的predicates:EXPLAIN表示在将数据传输到Impala后执行测试。

explain select count(*) from hbase_table where year_registered = 2010;
+------------------------------------------------------------------------------------+
| Explain String                                                                     |
+------------------------------------------------------------------------------------+
...

| 01:AGGREGATE                                                                       |
| |  output: count(*)                                                                |
| |                                                                                  |
| 00:SCAN HBASE [hbase.hbase_table]                                                  |
|    predicates: year_registered = 2010                                              |
+------------------------------------------------------------------------------------+

如果将键列与任何非常量值进行比较,同样会导致效率低下。在这里,即使键列是STRING,并且使用相等运算符进行测试,Impala也必须扫描整个HBase表,因为键列与另一个列值而不是常量进行比较。

explain select count(*) from hbase_table where cust_id = private_email_address;
+------------------------------------------------------------------------------------+
| Explain String                                                                     |
+------------------------------------------------------------------------------------+
...

| 01:AGGREGATE                                                                       |
| |  output: count(*)                                                                |
| |                                                                                  |
| 00:SCAN HBASE [hbase.hbase_table]                                                  |
|    predicates: cust_id = private_email_address                                    |
+------------------------------------------------------------------------------------+

目前,使用ORorIN子句对行键的测试也没有优化为直接查找。将来可能会取消此类限制,因此请始终检查EXPLAIN输出以确保特定SQL构造是否会导致对HBase表的有效查询。

explainselectcount(*)fromhbase_tablewhere
cust_id='some_user@example.com'orcust_id='other_user@example.com';
+----------------------------------------------------------------------------------------+
|ExplainString|
+----------------------------------------------------------------------------------------+
explain select count(*) from hbase_table where
  cust_id = 'some_user@example.com' or cust_id = 'other_user@example.com';
+----------------------------------------------------------------------------------------+
| Explain String                                                                         |
+----------------------------------------------------------------------------------------+
...

| 01:AGGREGATE                                                                           |
| |  output: count(*)                                                                    |
| |                                                                                      |
| 00:SCAN HBASE [hbase.hbase_table]                                                      |
|    predicates: cust_id = 'some_user@example.com' OR cust_id = 'other_user@example.com' |
+----------------------------------------------------------------------------------------+

explain select count(*) from hbase_table where
  cust_id in ('some_user@example.com', 'other_user@example.com');
+------------------------------------------------------------------------------------+
| Explain String                                                                     |
+------------------------------------------------------------------------------------+
...

| 01:AGGREGATE                                                                       |
| |  output: count(*)                                                                |
| |                                                                                  |
| 00:SCAN HBASE [hbase.hbase_table]                                                  |
|    predicates: cust_id IN ('some_user@example.com', 'other_user@example.com')      |
+------------------------------------------------------------------------------------+

将每个值重写为单独的查询并在应用程序中组合结果,或者使用UNIONALL组合单行查询:

select count(*) from hbase_table where cust_id = 'some_user@example.com';
select count(*) from hbase_table where cust_id = 'other_user@example.com';

explain
  select count(*) from hbase_table where cust_id = 'some_user@example.com'
  union all
  select count(*) from hbase_table where cust_id = 'other_user@example.com';
+------------------------------------------------------------------------------------+
| Explain String                                                                     |
+------------------------------------------------------------------------------------+
...

| |  04:AGGREGATE                                                                    |
| |  |  output: count(*)                                                             |
| |  |                                                                               |
| |  03:SCAN HBASE [hbase.hbase_table]                                               |
| |     start key: other_user@example.com                                            |
| |     stop key: other_user@example.com\0                                           |
| |                                                                                  |
| 10:MERGE                                                                           |
...

| 02:AGGREGATE                                                                       |
| |  output: count(*)                                                                |
| |                                                                                  |
| 01:SCAN HBASE [hbase.hbase_table]                                                  |
|    start key: some_user@example.com                                                |
|    stop key: some_user@example.com\0                                               |
+------------------------------------------------------------------------------------+

Java HBase应用程序的配置选项

如果您有一个HBaseJava应用程序调用org.apache.hadoop.hbase.client.Scan类的setCacheBlockssetCaching方法,您可以通过Impala查询选项设置这些相同的缓存行为,以控制HBaseRegionServer上的内存压力。例如,在HBase中执行导致全表扫描(默认情况下对HBase效率低下)的查询时,您可以通过关闭该设置并为该设置指定一个大数字来减少内存使用并加快查询速度。HBASE_CACHE_BLOCKSHBASE_CACHING

要设置这些选项,请在impala-shell中发出如下命令:

-- Same as calling setCacheBlocks(true) or setCacheBlocks(false).
set hbase_cache_blocks=true;
set hbase_cache_blocks=false;

-- Same as calling setCaching(rows).
set hbase_caching=1000;

或更新impalad默认文件在/etc/默认/黑斑羚,包括设置HBASE_CACHE_BLOCKS和/或HBASE_CACHING-default_query_options设定的IMPALA_SERVER_ARGS

注意:在Impala2.0及更高版本中,这些选项可通过JDBC或ODBC接口使用该SET语句进行设置。

通过Impala查询HBase的用例

以下是使用Impala查询HBase表的代表性用例:

  • 使用HBase来存储快速递增的计数器,例如网页被查看了多少次,或者在社交网络上,用户有多少连接或帖子收到了多少票。HBase可以有效地捕获此类可变数据:仅追加存储机制对于将每个更改写入磁盘是有效的,并且查询始终返回最新值。应用程序可以从HBase查询这样的特定总数,并将结果与​​从Impala查询的更广泛的数据集结合起来。
  • 在HBase中存储非常宽的表。宽表有许多列,可能有数千列,通常记录重要主题(例如在线服务的用户)的许多属性。这些表也往往是稀疏的,即大部分列的值为NULL、0、false、空字符串或其他空白或占位符值。(例如,任何特定网站用户可能从未使用过某些网站功能、填写了他们个人资料中的某个字段、访问了网站的特定部分等。)针对此类表的典型查询是查看向上一行以检索有关特定主题的所有信息,而不是像在典型的Impala管理的表中那样汇总、平均或过滤数百万行。

将数据加载到HBase表中

ImpalaINSERT语句适用于HBase表。该INSERT...VALUES语法非常适合HBase表,因为插入单行是HBase表的有效操作。(对于常规Impala表,数据文件在HDFS中,由产生的微小数据文件INSERT...VALUES效率极低,因此您不会对包含任何重要数据量的表使用该技术。)

当您使用该INSERT...SELECT语法时,HBase表中的结果可能比您预期的要少。HBase只存储每个唯一行键的最新版本,因此如果INSERT...SELECT语句复制包含键列相同值的多行,后续查询将只返回具有每个键列值的一行:

虽然Impala没有UPDATE语句,但您可以通过INSERT每次对键列使用相同的值执行连续语句来达到相同的效果:

Impala和HBase集成的限制和限制

Impala与HBase的集成有以下限制和限制,一些继承自HBase和Hive之间的集成,一些是Impala独有的:

  • 如果您DROPTABLE为映射到HBase表的内部(Impala管理)表发出a,则不会在HBase中删除基础表。DROPTABLE在这种情况下,Hive语句还会删除HBase表。
  • INSERTOVERWRITE语句不适用于HBase表。您可以插入新数据,或通过插入具有相同键值的新行来修改现有行,但不能替换表的全部内容。INSERTOVERWRITE如果您需要此功能,您可以在Hive中执行。
  • 如果CREATETABLELIKE对映射到HBase表的表发出语句,则新表也是HBase表,但继承与原始表相同的底层HBase表名称。新表实际上是旧表的别名,而不是具有相同列结构的新表。避免CREATETABLELIKE用于HBase表,以避免任何混淆。
  • 使用ImpalaINSERT...SELECT语法将数据复制到HBase表中可能会产生比查询结果集中更少的新行。如果结果集包含多个键列具有相同值的行,则每一行将取代之前具有相同键值的任何行。由于插入行的顺序是不可预测的,因此您不能依赖此技术来保留特定键值的“最新”版本。
  • 由于Impala2.3及更高版本中可用的复杂数据类型(ARRAYSTRUCTMAP)目前仅在Parquet表中受支持,因此您不能在通过Impala查询的HBase表中使用这些类型。
  • LOADDATA语句不能与HBase表一起使用。
  • 该语句的TABLESAMPLE子句SELECT不适用于从视图、子查询或真实基表以外的任何内容派生的表引用。该子句仅适用于由HDFS或类似HDFS的数据文件支持的表,因此不适用于Kudu或HBase表。

从Impala查询HBase表的示例

下面的例子创建一个有四个列族的HBase表,通过Hive创建一个对应的表,然后通过Impala插入和查询该表。

在HBaseshell中,表名在CREATEDROP语句中被引用。在HBase中创建的表以“启用”状态开始;在通过HBaseshell删除它们之前,您必须发出声明。disable'table_name'

$ hbase shell
15/02/10 16:07:45
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
...
hbase(main):001:0> create 'hbasealltypessmall', 'boolsCF', 'intsCF', 'floatsCF', 'stringsCF'
0 row(s) in 4.6520 seconds
=> Hbase::Table - hbasealltypessmall
hbase(main):006:0> quit

CREATETABLE在Hiveshell中发出以下语句。(ImpalaCREATETABLE语句当前不支持该STOREDBY子句,因此您切换到Hive以创建表,然后返回到Impala和impala-shell解释器来发出查询。)

此示例创建一个映射到HBase表的外部表,可供Impala和Hive使用。它被定义为一个外部表,因此当被Impala或Hive删除时,根本不会触及原始HBase表。

WITHSERDEPROPERTIES子句指定第一列(ID)表示行键,并将SQL表的其余列映射到HBase列族。映射依赖于表中列的顺序,而不是CREATETABLE语句中的列名。第一列定义为查找键;的STRING数据类型产生用于HBase的表最快基于密钥的查找。

注意:对于带有HBase表的Impala,确保良好性能的最重要方面是使用STRING列作为行键,如本示例所示。

$ hive
...
hive> use hbase;
OK
Time taken: 4.095 seconds
hive> CREATE EXTERNAL TABLE hbasestringids (
    >   id string,
    >   bool_col boolean,
    >   tinyint_col tinyint,
    >   smallint_col smallint,
    >   int_col int,
    >   bigint_col bigint,
    >   float_col float,
    >   double_col double,
    >   date_string_col string,
    >   string_col string,
    >   timestamp_col timestamp)
    > STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
    > WITH SERDEPROPERTIES (
    >   "hbase.columns.mapping" =
    >   ":key,boolsCF:bool_col,intsCF:tinyint_col,intsCF:smallint_col,intsCF:int_col,intsCF:\
    >   bigint_col,floatsCF:float_col,floatsCF:double_col,stringsCF:date_string_col,\
    >   stringsCF:string_col,stringsCF:timestamp_col"
    > )
    > TBLPROPERTIES("hbase.table.name" = "hbasealltypessmall");
OK
Time taken: 2.879 seconds
hive> quit;

一旦建立了到HBase表的映射,就可以从Impala发出DML语句和查询。以下示例显示了一系列INSERT语句,后跟一个查询。从性能的角度来看,理想的查询类型是基于映射到字符串列的行键从表中检索一行。初始语句使通过Hive创建的表对Impala可见。INVALIDATEMETADATAtable_name

$ impala-shell -i localhost -d hbase
Starting Impala Shell without Kerberos authentication
Connected to localhost:21000
...
Query: use `hbase`
[localhost:21000] > invalidate metadata hbasestringids;
Fetched 0 row(s) in 0.09s
[localhost:21000] > desc hbasestringids;
+-----------------+-----------+---------+
| name            | type      | comment |
+-----------------+-----------+---------+
| id              | string    |         |
| bool_col        | boolean   |         |
| double_col      | double    |         |
| float_col       | float     |         |
| bigint_col      | bigint    |         |
| int_col         | int       |         |
| smallint_col    | smallint  |         |
| tinyint_col     | tinyint   |         |
| date_string_col | string    |         |
| string_col      | string    |         |
| timestamp_col   | timestamp |         |
+-----------------+-----------+---------+
Fetched 11 row(s) in 0.02s
[localhost:21000] > insert into hbasestringids values ('0001',true,3.141,9.94,1234567,32768,4000,76,'2014-12-31','Hello world',now());
Inserted 1 row(s) in 0.26s
[localhost:21000] > insert into hbasestringids values ('0002',false,2.004,6.196,1500,8000,129,127,'2014-01-01','Foo bar',now());
Inserted 1 row(s) in 0.12s
[localhost:21000] > select * from hbasestringids where id = '0001';
+------+----------+------------+-------------------+------------+---------+--------------+-------------+-----------------+-------------+-------------------------------+
| id   | bool_col | double_col | float_col         | bigint_col | int_col | smallint_col | tinyint_col | date_string_col | string_col  | timestamp_col                 |
+------+----------+------------+-------------------+------------+---------+--------------+-------------+-----------------+-------------+-------------------------------+
| 0001 | true     | 3.141      | 9.939999580383301 | 1234567    | 32768   | 4000         | 76          | 2014-12-31      | Hello world | 2015-02-10 16:36:59.764838000 |
+------+----------+------------+-------------------+------------+---------+--------------+-------------+-----------------+-------------+-------------------------------+
Fetched 1 row(s) in 0.54s

注意:在Hive中创建表后,例如本示例中的HBase映射表,下次连接到Impala时发出语句,让Impala知道新表。(在Impala1.2.4之前,如果Impala还不知道该表,则无法指定表名;在Impala1.2.4及更高版本中,指定表名可避免为其他未更改的表重新加载元数据。)INVALIDATEMETADATAtable_name