CREATE EXTERNAL TABLE my_mapping_table
STORED AS KUDU
TBLPROPERTIES (
'kudu.table_name' = 'my_kudu_table'
);
Kudu与Apache Impala紧密集成,允许您在Impala使用Impala的SQL语法从Kudu去做 插入,查询,更新和删除数据,作为使用Kudu API 构建自定义Kudu应用程序的替代方法。另外,使用Impala作为代理,您可以使用JDBC或ODBC来使用任何语言、框架或BI工具编写的现有或新应用程序连接到Kudu数据。
此文档特定于Impala的某些版本。描述的语法将仅在以下版本中运行:
CDH 5.10附带的Impala 2.7.0版本。SELECT VERSION()
将报告impalad version 2.7.0-cdh5.10.0
。
从源代码编译的Apache Impala 2.8.0版本。SELECT VERSION()
将报告impalad version 2.8.0
。
较早版本的Impala 2.7(包括IMPALA_KUDU
以前提供的特殊版本)具有不兼容的语法。未来版本可能与此语法兼容,但我们建议您检查这是您安装的相应版本对应的最新可用文档。
本文档不描述Impala安装过程。请参阅博主的Impala安装文档。
Kudu内不需要进行配置更改,从而可以访问Impala。
虽然不是绝对必要的,但建议您配置Impala与Kudu Master服务器的位置-(可以跳过该步骤):
--kudu_master_hosts=
在Impala服务配置中设置该标志。如果您正在使用Cloudera Manager,请参阅相应的Cloudera Manager文档。
如果在Impala服务中未设置此标志,则每次通过kudu_master_addresses
在TBLPROPERTIES
子句中指定属性创建表时,都需要手动提供此配置 。
本指南的其余部分假定配置已设置。
这只是Impala Shell功能的一小部分。有关更多详细信息,请参阅 Impala Shell文档。 |
使用该impala-shell
命令启动Impala Shell 。默认情况下,impala-shell
尝试连接到localhost
端口21000 上的Impala守护程序。要连接到其他主机,请使用该-i
选项。要自动连接到特定的Impala数据库,请使用该-d
选项。例如,如果您的所有Kudu表都位于数据库中的Impala中impala_kudu
,则-d impala_kudu
可以使用此数据库。
要退出Impala Shell,请使用以下命令: quit;
使用Impala创建新的Kudu表时,可以将该表创建为内部表或外部表。
内部表由Impala管理,当您从Impala中删除时,数据和表确实被删除。当您使用Impala创建新表时,它通常是内部表。
外部表(创建者CREATE EXTERNAL TABLE
)不受Impala管理,并且删除此表不会将表从其源位置(此处为Kudu)丢弃。相反,它只会去除Impala和Kudu之间的映射。这是Kudu提供的用于将现有表映射到Impala的语法。
有关内部和外部表的更多信息,请参阅 Impala文档。
通过Kudu API或其他集成(如Apache Spark)创建的表不会在Impala中自动显示。要查询它们,您必须先在Impala中创建外部表以将Kudu表映射到Impala数据库中:
CREATE EXTERNAL TABLE my_mapping_table
STORED AS KUDU
TBLPROPERTIES (
'kudu.table_name' = 'my_kudu_table'
);
从Impala在Kudu创建一个新表类似于将现有的Kudu表映射到Impala表,只是您需要自己指定模式和分区信息。
使用以下示例作为指导。Impala首先创建表,然后创建映射。
CREATE TABLE my_first_table1 ( id BIGINT, name STRING, PRIMARY KEY(id) # 使用PRIMARY KEY(id) 而不是 'kudu.key_columns' = 'id'
)PARTITION BY HASH PARTITIONS 16 STORED AS KUDU
TBLPROPERTIES ( 'kudu.table_name' = 'my_kudu_table', 'kudu.master_addresses' = 'biyuzhe:7051' );
# 语句顺序不能乱
# STORED AS KUDU 是kudo表的标识
# 一定要指定'kudu.master_addresses',否则报错
在CREATE TABLE
语句中,必须首先列出构成主键的列。此外,主键列被隐式标记NOT NULL
。
创建新的Kudu表时,需要指定一个分发方案。请参阅分区表。id
为了简单起见,上面的表创建示例通过散列 列分成16个分区。有关分区的指导,请参阅 Thumb的“分区规则”。
CREATE TABLE AS SELECT
您可以使用CREATE TABLE … AS SELECT
语句通过查询Impala中的任何其他表或表来创建表。以下示例将所有行从现有表 old_table
导入到Kudu表中new_table
。列的名称和类型 将根据new_table
SELECT
语句的结果集中的列确定。请注意,您必须另外指定主键和分区。
CREATE TABLE new_table
PRIMARY KEY (ts, name)
PARTITION BY HASH(name) PARTITIONS 8
STORED AS KUDU
AS SELECT ts, name, value FROM old_table;
每个表分为由一个或多个表服务器提供的表。理想情况下, tablets should split a table’s data 平等地拆分表的数据。Kudu目前没有自动(或手动)拆分预先存在的 tablet的机制。在实现此功能之前,您必须在创建表时指定分区。在设计表格架构时,请考虑使用主键,您可以将表格分割成以相似速率增长的分区。PARTITION BY
使用Impala创建表时,可以使用子句指定分区:
Impala关键字,例如group ,当它们使用''单引号括起来时,在关键字意义上不被使用。 |
# 根据id的值域划分分区实例
CREATE TABLE cust_behavior ( _id BIGINT PRIMARY KEY, salary STRING, edu_level INT, usergender STRING, `group` STRING, city STRING, postcode STRING, last_purchase_price FLOAT, last_purchase_date BIGINT, category STRING, sku STRING, rating INT, fulfilled_date BIGINT ) PARTITION BY RANGE (_id) ( PARTITION VALUES < 1439560049342, PARTITION 1439560049342 <= VALUES < 1439566253755, PARTITION 1439566253755 <= VALUES < 1439572458168, PARTITION 1439572458168 <= VALUES < 1439578662581, PARTITION 1439578662581 <= VALUES < 1439584866994, PARTITION 1439584866994 <= VALUES < 1439591071407, PARTITION 1439591071407 <= VALUES ) STORED AS KUDU;
如果您有多个主键列,则可以使用元组语法指定分区边界:('va',1), ('ab',2)
。该表达式必须是有效的JSON。
每个Impala表都包含在称为数据库的命名空间中。调用默认数据库default
,用户可以根据需要创建和删除其他数据库。
当从Impala中创建一个受管理的Kudu表时,相应的Kudu表将被命名my_database::table_name
。
创建Kudu表时不支持以下Impala关键字: - PARTITIONED
- LOCATION
-ROWFORMAT
如果WHERE
查询的子句包括与comparisons的比较 =
,<=
“\ <”,“\>”, ,>=
,BETWEEN
或IN
,kudo直接评估条件,并且仅返回相关的结果。这提供了最佳性能,因为Kudu只将相关结果返回给Impala。而对于由Impala支持的谓词!=
,LIKE
或其他谓词类型,Kudu不会直接评估这些谓词,而是将所有结果返回给Impala,并依赖于Impala来评估剩余的谓词并相应地过滤结果。这可能会导致性能上的差异,这取决于评估WHERE
子句之前和之后的结果集的增量。
-- 就是说,对于Kudo支持的条件判断,Kudo自己来完成。完成不了的就丢给Impala。
根据主键列上的分区模式将表格划分为 tablets。每个 tablet 由至少一台 tablet server 提供。理想情况下,a table 应该分成 tablets ,分散在多个服务器上,以最大化并行运行。您使用的分区方案的详细信息将完全取决于您存储的数据类型和访问方式。关于Kudu模式设计的全面讨论,请参见Schema Design。
Kudu目前没有在创建表之后拆分或合并 tablets 的机制。创建表时,必须为表提供一个分区模式。在设计表格时,请考虑使用主键,这样您就可以将表格分为以相同速率增长的 tablets 。
您可以使用Impala的PARTITION BY
关键字对表进行分区,该关键字支持通过RANGE
或HASH来分配
。分区方案可以包含零个或多个HASH
定义,后跟可选RANGE
定义。该RANGE
定义可以引用一个或多个主键列。的实例基本和高级 分区如下所示。
PARTITION BY RANGE
您可以为一个或多个主键列指定范围分区。Kudu中的范围分区允许根据所选分区键的特定值或值的范围拆分表。这使您能够平衡写入中的并行性与扫描效率。
假设你有一个具有列的表state
,name
和purchase_count
。以下示例创建50个tablets,每个state一个。
单调增加VALUE的情况
如果您在其值单调递增的列上按范围分区,则最后一个tablets的增长将远大于其他平台。此外,插入的所有数据将一次写入单个tablets,限制了数据摄取的可扩展性。在这种情况下,请考虑分配 |
CREATE TABLE customers (
state STRING,
name STRING,
purchase_count int,
PRIMARY KEY (state, name)
)
PARTITION BY RANGE (state)
(
PARTITION VALUE = 'al',
PARTITION VALUE = 'ak',
PARTITION VALUE = 'ar',
-- ... etc ...
PARTITION VALUE = 'wv',
PARTITION VALUE = 'wy'
)
STORED AS KUDU;
PARTITION BY HASH
若不是通过明确的范围分发,或者与范围分布相结合,您可以通过哈希分发到特定数量的“buckets”。指定您要分区的主键列以及要使用的存储桶数。通过散列指定的键列来分配行。假设散列的值本身并不表现出显着的偏差,这将有助于将数据均匀地分布在数据桶之间。
您可以指定多个定义,您可以指定使用复合主键的定义。但是,在多个散列定义中不能提及一列。Consider two columns, a
and b
: * HASH(a)
, HASH(b)
* 、 HASH(a,b)
* 后面两个是错误的表达:HASH(a), HASH(a,b)
PARTITION BY HASH 没有指定列是通过散列所有主键列来创建所需数量的桶的快捷方式。 |
如果主键值均匀分布在其域中,并且数据偏移不明显,例如时间戳或序列号,则哈希分区是一种合理的方法。
以下示例通过散列id
和sku
列创建16个tablets。这传播了所有16个tablets的写操作。在此示例中,对一系列sku
值的查询可能需要读取所有16个tablets,因此可能不是此表的最佳模式。有关扩展示例,请参阅高级分区。
CREATE TABLE cust_behavior (
id BIGINT,
sku STRING,
salary STRING,
edu_level INT,
usergender STRING,
`group` STRING,
city STRING,
postcode STRING,
last_purchase_price FLOAT,
last_purchase_date BIGINT,
category STRING,
rating INT,
fulfilled_date BIGINT,
PRIMARY KEY (id, sku)
)
PARTITION BY HASH PARTITIONS 16 #根据数据自行指定的一个数
STORED AS KUDU;
您可以组合HASH
和RANGE
分区以创建更复杂的分区模式。您可以指定零个或多个HASH
定义,后跟零或一个RANGE
定义。每个定义可以包含一个或多个列。虽然枚举每一个可能的分发模式都超出了本文档的范围,但是有几个例子说明了一些可能性。
PARTITION BY HASH
和 RANGE
考虑上面的简单哈希示例,如果您经常查询一系列sku
值,则可以通过将哈希分区与范围分区相结合来优化示例。
以下示例仍然会创建16个tablets,首先将id
列分成4个存储区,然后根据sku
字符串的值将应用范围分区拆分为四个tablets。文字分散在至少四片(最多可达16张)。当您查询连续的sku
值范围时,您很可能只需要从四分之一的平板电脑中读取即可完成查询。
默认情况下,使用时,整个主键将被散列PARTITION BY HASH 。只能在主键上进行哈希,通过使用类似的语法来指定它PARTITION BY HASH (id, sku) 。 |
CREATE TABLE cust_behavior (
id BIGINT,
sku STRING,
salary STRING,
edu_level INT,
usergender STRING,
`group` STRING,
city STRING,
postcode STRING,
last_purchase_price FLOAT,
last_purchase_date BIGINT,
category STRING,
rating INT,
fulfilled_date BIGINT,
PRIMARY KEY (id, sku)
)
PARTITION BY HASH (id) PARTITIONS 4, #首先将id
列分成4个存储区
RANGE (sku) #然后根据sku
字符串的值将应用范围分区拆分为四个tablets
(
PARTITION VALUES < 'g',
PARTITION 'g' <= VALUES < 'o',
PARTITION 'o' <= VALUES < 'u',
PARTITION 'u' <= VALUES
)
STORED AS KUDU;
PARTITION BY HASH
定义
再次扩展上面的例子,假设查询模式是不可预测的,但是您希望确保写入 分布在大量tablets上您可以通过在主键列上进行散列来实现整个主键的最大分配。
CREATE TABLE cust_behavior (
id BIGINT,
sku STRING,
salary STRING,
edu_level INT,
usergender STRING,
`group` STRING,
city STRING,
postcode STRING,
last_purchase_price FLOAT,
last_purchase_date BIGINT,
category STRING,
rating INT,
fulfilled_date BIGINT,
PRIMARY KEY (id, sku)
)
PARTITION BY HASH (id) PARTITIONS 4,
HASH (sku) PARTITIONS 4 #该示例创建16个分区,也可以使用HASH (id, sku) PARTITIONS 16
STORED AS KUDU;
该示例创建16个分区。你也可以使用HASH (id, sku) PARTITIONS 16
。然而,对sku
值的扫描几乎总是影响所有16个分区,而不是可能限于4。
Kudu 1.0及更高版本支持使用非覆盖范围分区,其解决方案如下:
非覆盖的 range 范围分区,在需要考虑不断增加的主键的时间序列数据或其他模式的情况下,服务旧数据的tablets的大小相对固定,而接收新数据的tablet 将不受限制地增长。
如果您要根据其类别(如销售区域或产品类型)对数据进行分区,而不使用未覆盖的范围分区,则必须提前知道所有分区,或者如果要添加或删除分区,请手动重新创建表。 ,例如引入或消除产品类型。
非覆盖范围分区有一些注意事项。请务必阅读链接:/docs/schema_design.html [Schema Design guide]。
此示例每年创建一个tablets(共5个tablets),用于存储日志数据。该表仅接受2012年至2016年的数据。这些范围之外的键将被拒绝。
CREATE TABLE sales_by_year (
year INT, sale_id INT, amount INT,
PRIMARY KEY (sale_id, year) ) PARTITION BY RANGE (year) ( PARTITION VALUE = 2012, PARTITION VALUE = 2013, PARTITION VALUE = 2014, PARTITION VALUE = 2015, PARTITION VALUE = 2016 ) STORED AS KUDU;
当记录在2017年开始进入时,将被拒绝。在这一点上,2017
应该增加如下范围:
ALTER TABLE sales_by_year ADD RANGE PARTITION VALUE = 2017;
在需要滚动窗口的数据保留的用例中,范围分区也可能被丢弃。例如,如果不再保留2012年的数据,则可能会批量删除数据:
ALTER TABLE sales_by_year DROP RANGE PARTITION VALUE = 2012;
请注意,就像删除表一样,这无形中删除了存储在丢弃分区中的所有数据。
对于大型tablets,如 fact事务表,目标是在集群中拥有核心数量的tablets。
对于小的tablets(如维度表),目标是使用足够数量的tablets,每个tablets的大小至少为1 GB。
一般来说,请注意,在当前的实现中,tablets的数量限制了读取的并行性。增加tablets数量超过核心数量可能会减慢查询速度。
Impala允许您使用标准SQL语法将数据插入Kudu。
此示例插入单个行。
INSERT INTO my_first_table VALUES (99, "sarah");
此示例使用单个语句插入三行。
INSERT INTO my_first_table VALUES (1, "john"), (2, "jane"), (3, "jim");
批量插入时,至少有三种常用选择。每个可能有优点和缺点,具体取决于您的数据和情况。
INSERT
语句
这种方法具有易于理解和实现的优点。这种方法可能是低效的,因为Impala与Kudu的插入性能相比具有很高的查询启动成本。这将导致相对较高的延迟和较差的吞吐量。
INSERT
语句与多个
VALUES
INSERT如果包含超过1024个VALUES
语句,则batch_size
在将请求发送给Kudu之前,Impala将其批量分组为1024(or the value of batch_size
)。这种方法可能会比多个顺序语句执行得稍微好一些。要设置当前Impala Shell会话的批量大小,请使用以下语法:set batch_size=10000;
增加Impala batch_size会导致Impala使用更多的内存。您应该验证对群集的影响并进行相应调整。 |
从Impala和Kudu的角度来看,通常表现最好的方法通常是使用SELECT FROM
Impala中的语句导入数据。
如果您的数据尚未在Impala中,则一种策略是从文本文件(如TSV或CSV文件)中 导入。
创建Kudu表,注意指定为主键的列不能为空值。
通过查询包含原始数据的表将值插入到Kudu表中,如下例所示:
INSERT INTO my_kudu_table
SELECT * FROM legacy_data_import_table;
在许多情况下,适当的摄取路径是使用C ++或Java API直接插入Kudu表。与其他Impala表不同,通过API插入到Kudu表中的数据可用于Impala中的查询,而不需要INVALIDATE METADATA
其他Impala存储类型所需的任何语句或其他语句。
INSERT
和主键唯一性规则在大多数关系数据库中,如果您尝试插入已插入的行,则插入将失败,因为主键已经重复。见 Failures During INSERT
,UPDATE
和DELETE
Operations。然而,Impala不会使查询失败。相反,它会生成一个警告,但是继续执行insert语句的其余部分。
如果插入的行意图替换现有行,UPSERT
则可以使用替代INSERT
。
INSERT INTO my_first_table VALUES (99, "sarah");
UPSERT INTO my_first_table VALUES (99, "zoe");
-- the current value of the row is 'zoe'
UPDATE my_first_table SET name="bob" where id = 3;
该UPDATE 声明仅在目标表位于Kudu时在Impala中使用才有效。 |
您可以使用“ 插入批量”中概述的相同方法批量更新 。
UPDATE my_first_table SET name="bob" where age > 10;
DELETE FROM my_first_table WHERE id < 3;
您也可以使用更复杂的语法进行删除。FROM
子条款中的逗号是Impala指定连接查询的一种方法。有关Impala连接的更多信息,请参阅http://www.cloudera.com/content/cloudera/en/documentation/core/latest/topics/impala_joins.html。
DELETE c FROM my_second_table c, stock_symbols s WHERE c.name = s.symbol;
该DELETE 声明仅在目标表位于Kudu时在Impala中有效。 |
您可以使用“ 插入批量”中概述的相同方法批量删除 。
DELETE FROM my_first_table WHERE id < 3;
INSERT
,UPDATE
和DELETE
操作失败时INSERT
,UPDATE
和DELETE
语句不能被视为一个整体。如果这些操作之一失败,则可能已经创建了密钥(在这种情况下INSERT
),或者记录可能已经被另一个进程修改或删除(在UPDATE
或者是DELETE
)的情况下。您应该设计您的应用程序。
您可以通过更改表的属性来更改Impala与给定Kudu表相关的元数据。这些属性包括表名,Kudu主地址列表,以及表是否由Impala(内部)或外部管理。
ALTER TABLE my_table RENAME TO my_new_table;
使用该ALTER TABLE … RENAME 语句重命名表仅重命名Impala映射表,而不管该表是内部还是外部表。这样可以避免可能访问基础的Kudu表的其他应用程序的中断。 |
如果表是内部表,则可以通过更改kudu.table_name
属性来重命名底层的Kudu表:
ALTER TABLE my_internal_table
SET TBLPROPERTIES('kudu.table_name' = 'new_name')
如果另一个应用程序在Impala下重命名了Kudu表,则可以重新映射外部表以指向不同的Kudu表名称。
ALTER TABLE my_external_table_
SET TBLPROPERTIES('kudu.table_name' = 'some_other_kudu_table')
ALTER TABLE my_table
SET TBLPROPERTIES('kudu.master_addresses' = 'kudu-new-master.example.com:7051');
ALTER TABLE my_table SET TBLPROPERTIES('EXTERNAL' = 'TRUE');
如果表是在Impala中作为内部表创建的,则使用CREATE TABLE
标准DROP TABLE
语法会删除底层的Kudu表及其所有数据。如果表被创建为一个外部表,使用CREATE EXTERNAL TABLE
,Impala和Kudu之间的映射被删除,但Kudu表保持原样,并包含其所有数据。
DROP TABLE my_first_table;
上面的例子只是探索了Impala Shell所能做的一小部分。
了解Impala项目。
阅读Impala文档。
查看Impala SQL参考。
阅读Impala内部或了解如何在Impala Wiki上为Impala做出贡献。
阅读本土的Kudu API。
在Impala中用作外部表格时,必须使用包含大写字母或非ASCII字符的名称的Kudu表。
具有包含大写字母或非ASCII字符的列名称的Kudu表可能不会用作Impala中的外部表。专栏可能在Kudu更名为解决这个问题。
创建Kudu表时,该CREATE TABLE
语句必须以主键顺序包含其他列之间的主键列。
包含UNIXTIME_MICROS
类型列的Kudu表可能不会用作Impala中的外部表。
Impala不能创建Kudu表TIMESTAMP
,DECIMAL
,VARCHAR
,或嵌套类型的列。
Impala无法更新主键列中的值。
NULL
,NOT NULL
,!=
,和LIKE
谓词不被支持到Kudu,而是将由Impala扫描节点进行评估。这可能会降低相对于其他类型谓词的性能。
通过Impala的更新,插入和删除是非事务性的。如果一部分查询失败,其部分效果将不会回滚。
单个查询的最大并行度仅限于表中的tables数量。为了获得良好的分析性能,针对每个主机10个或更多个tables或使用large tables。