Impala 查询Kudu表


您可以使用Impala查询Apache Kudu表,此功能便于用户访问存储系统,该存储系统针对不同类型的工作负载进行了优化,而并非单纯只是Impala的默认设置。

默认情况下,Impala表使用各种文件格式的数据文件存储在HDFS上。HDFS文件非常适合批量加载(追加操作)和使用全表扫描的查询,但不支持就地更新或删除。

Kudu是Impala使用的替代存储引擎,它可以执行实时更新(用于混合读/写工作负载)和快速扫描(用于数据仓库/分析操作)。

将Kudu表与Impala结合使用可以避免额外的步骤,从而简化ETL管道

某些Impala SQL语句和条款,如DELETEUPDATEUPSERT,和PRIMARYKEY只使用Kudu表的工作。其他说明和条款,如LOADDATATRUNCATETABLEINSERTOVERWRITE,并不适用于Kudu表。

在Impala中使用Kudu表的好处

Kudu和Impala的组合最适用于扫描性能很重要但数据小批量或需要更新而不完全替换的表。HDFS支持的表可能需要大量开销来在新数据到达时替换或重组数据文件。

Impala可以在Kudu表内执行高效的查找和扫描,Impala还可以高效地执行更新或删除操作。

您还可以使用KuduJava、C++和PythonAPI在Impala之外进行摄取或转换操作,Impala可以随时查询当前数据。

配置Impala以与Kudu一起使用

-kudu_master_hosts配置属性必须正确的设置impalad守护进程,为CREATETABLE...STOREDASKUDU报表连接到相应的库杜服务器。通常,此设置所需的值为kudu_host:7051。在高可用性Kudu部署中,指定多个Kudu主机的名称,以逗号分隔。

如果-kudu_master_hosts未设置配置属性,你仍然可以为每个表的相应值通过指定相关联TBLPROPERTIES('kudu.master_addresses')的条款CREATETABLE语句或改变TBLPROPERTIES('kudu.master_addresses')与价值ALTERTABLE声明。

Kudu表的集群拓扑

使用HDFS支持的表,您通常关心集群中DataNode的数量、查询期间读取的HDFS数据文件的数量和大小,因此每个DataNode执行的工作量和网络通信结合中间结果并产生最终结果集。

使用Kudu表,拓扑考虑是不同的,因为:

  • 底层存储由Kudu管理和组织,不表示为HDFS数据文件。
  • Kudu处理数据分区的一些基础机制。您可以使用散列分区和范围分区的组合来指定分区方案,以便您可以决定在新数据到达时花费多少精力来管理分区。例如,您可以构建适用于日期范围的分区,而不是针对每天或每小时的单独分区。
  • 数据根据称为tablet的存储单元进行物理划分。平板电脑由平板电脑服务器存储。每个tablet服务器可以存储多个tablet,每个tablet跨多个tablet服务器复制,由Kudu自动管理。在可行的情况下,将平板电脑服务器与Impala守护进程放在同一主机上,尽管这不是必需的。

需要复制因子

默认情况下,通过Impala创建的Kudu表使用的平板复制因子为3。要更改Kudu表的复制因子,请在CREATETABLE语句语句中使用指定复制因子。TBLPROPERTIES('kudu.num_tablet_replicas'='n')

Kudu表的副本数必须是奇数。

kudu.num_tablet_replicas在创建表后更改属性当前无效。

Kudu表的ImpalaDDL增强(CREATETABLE和ALTERTABLE)

您可以使用ImpalaCREATETABLEALTERTABLE语句来创建和微调Kudu表的特征。由于Kudu表具有不适用于其他类型Impala表的功能和属性,因此请先熟悉与Kudu相关的概念和语法。

Kudu表的主键列

Kudu表首次向Impala引入了主键的概念。主键由一列或多列组成,这些列的值在查询过程中被组合并用作查找键。这些列表示的元组必须是唯一的,不能包含任何NULL值,并且一旦插入就永远无法更新。对于Kudu表,所有分区键列必须来自主键列集。

主键具有物理和逻辑两个方面:

  • 在物理方面,它用于将数据值映射到特定的平板电脑以进行快速检索。由于主键值形成的元组是唯一的,主键列通常具有高度选择性。
  • 在逻辑方面,唯一性约束允许您避免表中的重复数据。例如,如果INSERT操作中途失败,则表中可能只存在一些新行。您可以重新运行相同的INSERT,并且只会添加丢失的行。或者,如果表中的数据陈旧,您可以运行一条UPSERT语句来更新数据,而不会创建现有行的重复副本。

笔记:Impala只允许对Kudu表的列使用PRIMARYKEY子句和NOTNULL约束。这些限制在Kudu端强制执行。

CREATETABLE的Kudu特定列属性

以下部分提供了您可以在列定义中使用的一些特定于Kudu的关键字的更多详细信息。

CREATETABLE语句中的列列表可以包含以下属性,仅适用于Kudu表:

 PRIMARY KEY
| [NOT] NULL
| ENCODING codec
| COMPRESSION algorithm
| DEFAULT constant_expression
| BLOCK_SIZE number

有关每个列属性的详细信息,请参阅以下部分。

主键属性

Kudu表的主键是唯一标识每一行的一列或一组列。主键值还用作表中值的自然排序顺序。每行的主键值基于列值的组合。

由于所有主键列都必须具有非空值,因此在PRIMARYKEY子句中指定列会隐式地将NOTNULL属性添加到该列。

主键列必须是CREATETABLE语句中指定的第一个列。对于单列主键,您可以PRIMARYKEY在列定义中包含内联属性。对于多列主键,您可以在列列表的末尾包含一个子句作为单独的条目。PRIMARYKEY(c1,c2,...)

您可以PRIMARYKEY在单个列定义中内联指定属性,也可以在列列表末尾作为单独的子句指定该属性:

CREATE TABLE pk_inline
(
  col1 BIGINT PRIMARY KEY,
  col2 STRING,
  col3 BOOLEAN
) PARTITION BY HASH(col1) PARTITIONS 2 STORED AS KUDU;

CREATE TABLE pk_at_end
(
  col1 BIGINT,
  col2 STRING,
  col3 BOOLEAN,
  PRIMARY KEY (col1)
) PARTITION BY HASH(col1) PARTITIONS 2 STORED AS KUDU;

当主键是单列时,这两种形式是等价的。如果主键包含多个列,则必须使用列列表中的单独条目指定主键:

CREATE TABLE pk_multiple_columns
(
  col1 BIGINT,
  col2 STRING,
  col3 BOOLEAN,
  PRIMARY KEY (col1, col2)
) PARTITION BY HASH(col2) PARTITIONS 2 STORED AS KUDU;

SHOWCREATETABLE语句始终将PRIMARYKEY规范表示为列列表中的一个单独项目:

CREATE TABLE inline_pk_rewritten (id BIGINT PRIMARY KEY, s STRING)
  PARTITION BY HASH(id) PARTITIONS 2 STORED AS KUDU;

SHOW CREATE TABLE inline_pk_rewritten;
+------------------------------------------------------------------------------+
| result                                                                       |
+------------------------------------------------------------------------------+
| CREATE TABLE user.inline_pk_rewritten (                                      |
|   id BIGINT NOT NULL ENCODING AUTO_ENCODING COMPRESSION DEFAULT_COMPRESSION, |
|   s STRING NULL ENCODING AUTO_ENCODING COMPRESSION DEFAULT_COMPRESSION,      |
|   PRIMARY KEY (id)                                                           |
| )                                                                            |
| PARTITION BY HASH (id) PARTITIONS 2                                          |
| STORED AS KUDU                                                               |
| TBLPROPERTIES ('kudu.master_addresses'='host.example.com')                   |
+------------------------------------------------------------------------------+

主键的概念仅适用于Kudu表。每个Kudu表都需要一个主键。主键由一列或多列组成。您必须首先在列列表中指定任何主键列。

UPDATEorUPSERT语句不能更改主键列的内容。在主键中包含太多列(超过5或6个)也会降低写入操作的性能。因此,为主键规范选择最具选择性和最常测试的非空列。如果一列必须始终有一个值,但该值可能会在以后更改,请将其排除在主键之外,并NOTNULL为该列使用子句。如果现有行具有不正确或过时的键列值,请删除旧行并插入具有正确主键的全新行。

空|非空属性

对于Kudu表,您可以指定哪些列可以包含或不包含空值。此约束为Kudu表提供了额外级别的一致性强制。如果应用程序要求始终指定一个字段,请NOTNULL在相应的列定义中包含一个子句,Kudu会阻止NULL在该列中插入带有a的行。

例如,包含地理信息的表可能需要始终指定纬度和经度坐标。可能允许其他属性为NULL.例如,一个位置可能没有指定的地名,它的海拔高度可能不重要,它的人口可能最初未知,稍后填写。

由于所有主键列都必须具有非空值,因此在PRIMARYKEY子句中指定列会隐式地将NOTNULL属性添加到该列。

对于非Kudu表,Impala允许任何列包含NULL值,因为对可以使用外部工具和ETL过程准备的HDFS数据文件强制实施“非空”约束是不切实际的。

CREATE TABLE required_columns
(
  id BIGINT PRIMARY KEY,
  latitude DOUBLE NOT NULL,
  longitude DOUBLE NOT NULL,
  place_name STRING,
  altitude DOUBLE,
  population BIGINT
) PARTITION BY HASH(id) PARTITIONS 2 STORED AS KUDU;

在性能优化过程中,Kudu可以利用不允许空值跳过对每个输入行的某些检查的知识,加快查询和连接操作。因此,NOTNULL在适当的时候指定约束。

NULL子句是所有不属于主键的列的默认条件。您可以省略它,也可以指定它以阐明您已做出有意识的设计决定以允许列中存在空值。

由于主键列不能包含任何NULL值,因此主键列不需要该NOTNULL子句,但您仍然可以指定它以使您的代码具有自描述性。

默认属性

您可以为Kudu表中的列指定默认值。默认值可以是任何常量表达式,例如,文字值、算术和字符串运算的组合。它不能包含对列或非确定性函数调用的引用。

以下示例显示了DEFAULT子句的不同类型的表达式。使用常量值的要求意味着您可以填写占位符值,例如NULL、空字符串、0、-1'N/A'等,但不能引用函数或列名。因此,您不能使用DEFAULT诸如自动制作字符串值的大写副本、根据其他列的测试存储布尔值或从表示序列号的另一列中添加或减去一个值等操作。

CREATE TABLE default_vals
(
  id BIGINT PRIMARY KEY,
  name STRING NOT NULL DEFAULT 'unknown',
  address STRING DEFAULT upper('no fixed address'),
  age INT DEFAULT -1,
  earthling BOOLEAN DEFAULT TRUE,
  planet_of_origin STRING DEFAULT 'Earth',
  optional_col STRING DEFAULT NULL
) PARTITION BY HASH(id) PARTITIONS 2 STORED AS KUDU;

笔记:在设计一个全新的模式时,最好将NULL其用作任何未知或缺失值的占位符,因为这是数据库系统之间的通用约定。可以有效地存储空值,并且可以轻松地使用ISNULLISNOTNULL运算符进行检查。该DEFAULT属性适用于摄取已经具有用于表示未知或缺失值的既定约定的数据,或者绝大多数行具有一些常见的非空值的情况。

编码属性

Kudu表中的每一列都可以选择使用编码,这是一种降低磁盘大小的低开销压缩形式,然后需要额外的CPU周期来在查询期间重建原始值。通常,高度可压缩的数据受益于从磁盘读回数据的I/O减少。

Impala识别的编码关键字是:

  • AUTO_ENCODING:使用基于列类型的默认编码,数字类型列使用bitshuffle,字符串类型列使用字典。
  • PLAIN_ENCODING:保留原始二进制格式的值。
  • RLE:通过包含计数来压缩重复值(按主键顺序排序时)。
  • DICT_ENCODING:当不同字符串值的数量较少时,将原始字符串替换为数字ID。
  • BIT_SHUFFLE:重新排列值的位以有效地压缩基于主键顺序相同或仅略有不同的值序列。生成的编码数据也用LZ4压缩。
  • PREFIX_ENCODING:压缩字符串值中的公共前缀;主要在Kudu内部使用。

以下示例显示了表示编码类型的Impala关键字。(Impala关键字与Kudu中使用的符号名称匹配)。该DESCRIBE输出显示编码是如何表之后报告被创建,并且省略了编码(在这种情况下,对于ID列)是一样的指定DEFAULT_ENCODING

CREATE TABLE various_encodings
(
  id BIGINT PRIMARY KEY,
  c1 BIGINT ENCODING PLAIN_ENCODING,
  c2 BIGINT ENCODING AUTO_ENCODING,
  c3 TINYINT ENCODING BIT_SHUFFLE,
  c4 DOUBLE ENCODING BIT_SHUFFLE,
  c5 BOOLEAN ENCODING RLE,
  c6 STRING ENCODING DICT_ENCODING,
  c7 STRING ENCODING PREFIX_ENCODING
) PARTITION BY HASH(id) PARTITIONS 2 STORED AS KUDU;

-- Some columns are omitted from the output for readability.
describe various_encodings;
+------+---------+-------------+----------+-----------------+
| name | type    | primary_key | nullable | encoding        |
+------+---------+-------------+----------+-----------------+
| id   | bigint  | true        | false    | AUTO_ENCODING   |
| c1   | bigint  | false       | true     | PLAIN_ENCODING  |
| c2   | bigint  | false       | true     | AUTO_ENCODING   |
| c3   | tinyint | false       | true     | BIT_SHUFFLE     |
| c4   | double  | false       | true     | BIT_SHUFFLE     |
| c5   | boolean | false       | true     | RLE             |
| c6   | string  | false       | true     | DICT_ENCODING   |
| c7   | string  | false       | true     | PREFIX_ENCODING |
+------+---------+-------------+----------+-----------------+

压缩属性

您可以指定用于Kudu表中每一列的压缩算法。在检索值时,此ENCODING属性会比属性施加更多的CPU开销。因此,它主要用于具有长字符串的列,这些列不会从较便宜的ENCODING属性中受益很多。

适合自己的选择COMPRESSIONLZ4SNAPPYZLIB

笔记:使用BITSHUFFLE编码的列已经使用压缩LZ4,因此通常不需要任何附加COMPRESSION属性。

以下示例显示了STRING具有不同分布特征的多个列的设计注意事项,从而导致对ENCODINGCOMPRESSION属性进行选择。这些country值来自一组特定的字符串,因此该列非常适合用于字典编码。该post_id列包含一个升序的整数序列,其中几个前导位可能全为零,因此该列非常适合进行bitshuffle编码。这bodycolumn和翻译版本的相应列往往是很长的唯一字符串,不适用于任何编码方案,因此它们使用COMPRESSION属性代替。每种情况下的理想压缩编解码器都需要进行一些实验,以确定它提供了多少空间节省以及它根据实际数据增加了多少CPU开销。

CREATE TABLE blog_posts
(
  user_id STRING ENCODING DICT_ENCODING,
  post_id BIGINT ENCODING BIT_SHUFFLE,
  subject STRING ENCODING PLAIN_ENCODING,
  body STRING COMPRESSION LZ4,
  spanish_translation STRING COMPRESSION SNAPPY,
  esperanto_translation STRING COMPRESSION ZLIB,
  PRIMARY KEY (user_id, post_id)
) PARTITION BY HASH(user_id, post_id) PARTITIONS 2 STORED AS KUDU;

BLOCK_SIZE属性

虽然Kudu在内部不使用HDFS文件,因此不受HDFS块大小的影响,但它确实有一个称为块大小的I/O底层单位。该BLOCK_SIZE属性允许您为任何列设置块大小。

块大小属性是一个比较高级的特性。

Kudu表的分区

Kudu表使用特殊机制在底层平板服务器之间分配数据。尽管我们将此类表称为分区表,但它们与传统的Impala分区表的区别在于在CREATETABLE语句上使用了不同的子句。Kudu表使用PARTITIONBY,HASH,RANGE和range规范子句,而不是PARTITIONEDBYHDFS-backed表的子句,后者仅指定一个列名并为每个不同的值创建一个新分区。

有关Kudu分区机制的背景信息和架构详细信息,请参阅Kudu白皮书第3.2节

笔记:Kudu表的ImpalaDDL语法与使用Impala代码的实验性分支的早期Kudu版本不同。例如,DISTRIBUTEBY子句nowPARTITIONBY,子句now和范围分区语法被重新设计,以用涉及比较运算符的更具表现力的语法替换子句。INTOnBUCKETSPARTITIONSnSPLITROWS

哈希分区

哈希分区是Kudu表最简单的分区类型。对于散列分区捻表,插入的行的固定数目的之间划分“桶”通过将散列函数应用于所述列中指定的值HASH条款。散列确保具有相似值的行均匀分布,而不是聚集在同一个桶中。以这种方式在存储桶中传播新行可以让插入操作在多个平板服务器上并行工作。分离散列值可能会给查询带来额外的开销,其中使用基于范围的谓词的查询可能必须读取多个平板电脑才能检索所有相关值。

-- 1M rows with 50 hash partitions = approximately 20,000 rows per partition.
-- The values in each partition are not sequential, but rather based on a hash function.
-- Rows 1, 99999, and 123456 might be in the same partition.
CREATE TABLE million_rows (id string primary key, s string)
  PARTITION BY HASH(id) PARTITIONS 50
  STORED AS KUDU;

-- Because the ID values are unique, we expect the rows to be roughly
-- evenly distributed between the buckets in the destination table.
INSERT INTO million_rows SELECT * FROM billion_rows ORDER BY id LIMIT 1e6;

笔记:您可以使用PARTITIONS子句创建的最大桶数取决于集群中的平板电脑服务器的数量,而最小的是2个。为简单起见,CREATETABLE本节中的一些简单语句PARTITIONS2用于说明Kudu的最低要求桌子。对于大表,最好在集群中的每台服务器上使用大约10个分区。

范围分区

范围分区允许您根据一列或多列中的单个值或值范围精确指定分区。您RANGE可以在CREATETABLE语句后面添加一个或多个PARTITIONBY子句。

范围分区的Kudu表使用一个或多个范围子句,其中包括常量表达式VALUEVALUES关键字和比较运算符的组合。(此语法替换了SPLITROWS早期Kudu版本中使用的子句)。

-- 50 buckets, all for IDs beginning with a lowercase letter.
-- Having only a single range enforces the allowed range of values
-- but does not add any extra parallelism.
create table million_rows_one_range (id string primary key, s string)
  partition by hash(id) partitions 50,
  range (partition 'a' <= values < '{')
  stored as kudu;

-- 50 buckets for IDs beginning with a lowercase letter
-- plus 50 buckets for IDs beginning with an uppercase letter.
-- Total number of buckets = number in the PARTITIONS clause x number of ranges.
-- We are still enforcing constraints on the primary key values
-- allowed in the table, and the 2 ranges provide better parallelism
-- as rows are inserted or the table is scanned.
create table million_rows_two_ranges (id string primary key, s string)
  partition by hash(id) partitions 50,
  range (partition 'a' <= values < '{', partition 'A' <= values < '[')
  stored as kudu;

-- Same as previous table, with an extra range covering the single key value '00000'.
create table million_rows_three_ranges (id string primary key, s string)
  partition by hash(id) partitions 50,
  range (partition 'a' <= values < '{', partition 'A' <= values < '[', partition value = '00000')
  stored as kudu;

-- The range partitioning can be displayed with a SHOW command in impala-shell.
show range partitions million_rows_three_ranges;
+---------------------+
| RANGE (id)          |
+---------------------+
| VALUE = "00000"     |
| "A" <= VALUES < "[" |
| "a" <= VALUES < "{" |
+---------------------+

笔记:定义范围时,请注意避免“围栏错误”,其中可能会意外包含或省略极端值。例如,在前面代码清单中定义的表中,范围通过对所有以开头的值之后的最小值使用小于运算符来"a"<=VALUES<"{"确保包含以开头的任何值z,例如zaorzzz或。zzz-ZZZz

对于范围分区的Kudu表,必须存在适当的范围,然后才能在表中创建数据值。如果任何INSERTUPDATEUPSERT语句尝试创建超出指定范围的列值,则它们将失败。范围的错误检查在Kudu端执行;Impala将指定的范围信息传递给Kudu,如果范围无效,则返回任何错误或警告。(无意义的范围规范会导致DDL语句错误,但只会导致DML语句警告。)

范围可以是不连续的:

partition by range (year) (partition 1885 <= values <= 1889, partition 1893 <= values <= 1897)

partition by range (letter_grade) (partition value = 'A', partition value = 'B',
  partition value = 'C', partition value = 'D', partition value = 'F')

ALTERTABLE带有ADDPARTITIONorDROPPARTITION子句的语句可用于在现有Kudu表中添加或删除范围。

ALTER TABLE foo ADD PARTITION 30 <= VALUES < 50;
ALTER TABLE foo DROP PARTITION 1 <= VALUES < 5;

添加范围时,新范围不得与之前的任何范围重叠;也就是说,它只能填补之前范围内的空白。

alter table test_scores add range partition value = 'E';
alter table year_ranges add range partition 1890 <= values < 1893;

删除范围后,表中的所有关联行都将被删除。(无论表是内部的还是外部的,都是如此)。

alter table test_scores drop range partition value = 'E';
alter table year_ranges drop range partition 1890 <= values < 1893;

Kudu表还可以使用散列和范围分区的组合。

partition by hash (school) partitions 10,
  range (letter_grade) (partition value = 'A', partition value = 'B',
    partition value = 'C', partition value = 'D', partition value = 'F')

在Kudu表中使用分区

要查看Kudu表的当前分区方案,您可以使用SHOWCREATETABLE语句或SHOWPARTITIONS语句。此CREATETABLE语句显示的语法包括反映原始表结构的所有散列、范围或两者子句以及ALTERTABLE更改表结构的任何后续语句。

要查看Kudu表的底层存储桶和分区,请使用SHOWTABLESTATSorSHOWPARTITIONS语句。

使用Kudu处理日期、时间或时间戳数据

在Impala2.9及更高版本中,您可以TIMESTAMP在Kudu表中包含列,而不是将日期和时间表示为BIGINT值。TIMESTAMP对于Kudu表的行为有一些特殊的考虑:

  • Impala生成的原始96位值中的任何纳秒都不会存储,因为Kudu使用64位值表示日期/时间列。该值的纳秒部分是四舍五入的,而不是截断的。因此,TIMESTAMP您存储在Kudu表中的值可能与查询返回的值逐位不同。
  • Impala96位表示和Kudu64位表示之间的转换在读取或写入TIMESTAMP列时引入了一些性能开销。您可以通过KuduAPI执行插入来最小化写入期间的开销。由于读取期间的开销适用于每个查询,因此您可能会继续使用BIGINT列来表示性能关键应用程序中的日期/时间值。

ImpalaTIMESTAMP类型的年范围比基础Kudu数据类型更窄。Impala可以代表1400-9999年。如果非Impala客户端将超出此范围的年份值写入Kudu表,则Impala在查询期间NULL读取这些TIMESTAMP值时默认返回。或者,如果ABORT_ON_ERROR启用了查询选项,则查询会在遇到年份超出范围的值时失败。

--- Make a table representing a date/time value as TIMESTAMP.
-- The strings representing the partition bounds are automatically
-- cast to TIMESTAMP values.
create table native_timestamp(id bigint, when_exactly timestamp, event string, primary key (id, when_exactly))
  partition by hash (id) partitions 20,
  range (when_exactly)
  (
    partition '2015-01-01' <= values < '2016-01-01',
    partition '2016-01-01' <= values < '2017-01-01',
    partition '2017-01-01' <= values < '2018-01-01'
  )
  stored as kudu;

insert into native_timestamp values (12345, now(), 'Working on doc examples');

select * from native_timestamp;
+-------+-------------------------------+-------------------------+
| id    | when_exactly                  | event                   |
+-------+-------------------------------+-------------------------+
| 12345 | 2017-05-31 16:27:42.667542000 | Working on doc examples |
+-------+-------------------------------+-------------------------+

因为Kudu表在将TIMESTAMP列转换为Impala96位内部表示时有一些性能开销,对于性能关键的应用程序,您可以将日期/时间信息存储为自Unix纪元日期1月1日以来的秒数、毫秒数或微秒数,1970.BIGINT在ImpalaCREATETABLE语句中指定列,对应int64于基础Kudu表中的8字节整数(an)。然后根据需要使用Impala日期/时间转换函数来根据上下文生成数字TIMESTAMP、或STRING值。

例如,该unix_timestamp()函数返回一个整数结果,表示经过epoch的秒数。该now()函数产生一个TIMESTAMP代表当前日期和时间的,它可以作为参数传递给unix_timestamp()。表示日期和日期/时间的字符串文字可以TIMESTAMP转换为,并从那里转换为数值。以下示例展示了如何像BIGINT在Kudu表中一样存储日期/时间列,但TIMESTAMP为了方便起见,仍然使用字符串文字和值。

-- now() returns a TIMESTAMP and shows the format for string literals you can cast to TIMESTAMP.
select now();
+-------------------------------+
| now()                         |
+-------------------------------+
| 2017-01-25 23:50:10.132385000 |
+-------------------------------+

-- unix_timestamp() accepts either a TIMESTAMP or an equivalent string literal.
select unix_timestamp(now());
+------------------+
| unix_timestamp() |
+------------------+
| 1485386670       |
+------------------+

select unix_timestamp('2017-01-01');
+------------------------------+
| unix_timestamp('2017-01-01') |
+------------------------------+
| 1483228800                   |
+------------------------------+

-- Make a table representing a date/time value as BIGINT.
-- Construct 1 range partition and 20 associated hash partitions for each year.
-- Use date/time conversion functions to express the ranges as human-readable dates.
create table time_series(id bigint, when_exactly bigint, event string, primary key (id, when_exactly))
  partition by hash (id) partitions 20,
  range (when_exactly)
  (
    partition unix_timestamp('2015-01-01') <= values < unix_timestamp('2016-01-01'),
    partition unix_timestamp('2016-01-01') <= values < unix_timestamp('2017-01-01'),
    partition unix_timestamp('2017-01-01') <= values < unix_timestamp('2018-01-01')
  )
  stored as kudu;

-- On insert, we can transform a human-readable date/time into a numeric value.
insert into time_series values (12345, unix_timestamp('2017-01-25 23:24:56'), 'Working on doc examples');

-- On retrieval, we can examine the numeric date/time value or turn it back into a string for readability.
select id, when_exactly, from_unixtime(when_exactly) as 'human-readable date/time', event
  from time_series order by when_exactly limit 100;
+-------+--------------+--------------------------+-------------------------+
| id    | when_exactly | human-readable date/time | event                   |
+-------+--------------+--------------------------+-------------------------+
| 12345 | 1485386696   | 2017-01-25 23:24:56      | Working on doc examples |
+-------+--------------+--------------------------+-------------------------+

笔记:如果您执行涉及数字日期/时间值的高精度算术,则在将毫秒值除以1000或微秒值除以100万时,始终将整数分子转换为DECIMAL具有足够精度和小数位数的a以避免任何舍入或精度损失。

-- 1 million and 1 microseconds = 1.000001 seconds.
select microseconds,
  cast (microseconds as decimal(20,7)) / 1e6 as fractional_seconds
  from table_with_microsecond_column;
+--------------+----------------------+
| microseconds | fractional_seconds   |
+--------------+----------------------+
| 1000001      | 1.000001000000000000 |
+--------------+----------------------+

How Impala Handles Kudu Metadata

注意:本节仅适用未与HiveMetastore(HMS)集成的Kudu服务。

默认情况下,Kudu表的大部分元数据由底层存储层处理。Kudu表对Metastore数据库的依赖较少,并且在Impala端需要较少的元数据缓存。例如,Kudu表中的分区信息由Kudu管理,Impala不会缓存Kudu表的任何块位置元数据。如果Kudu服务未与HiveMetastore集成,Impala将管理HiveMetastore中的Kudu表元数据。

REFRESHINVALIDATEMETADATAHDFS支持的表相比,Kudu表需要更少的and语句。在Kudu表中添加、删除或更新数据时,这两个语句都不需要,即使更改是通过使用KuduAPI的客户端程序直接对Kudu进行的。仅在对Kudu表架构进行更改(例如添加或删除列)后运行或为Kudu表。REFRESHtable_nameINVALIDATEMETADATAtable_name

由于Kudu将自己的表的元数据与Metastore数据库分开管理,因此在Metastore数据库中存储了一个表名供Impala使用,在Kudu端有一个表名,这些名称可以通过ALTERTABLE语句独立修改。

为了避免潜在的名称冲突,前缀impala::和Impala数据库名称被编码到底层Kudu表名称中:

create database some_database;
use some_database;

create table table_name_demo (x int primary key, y int)
  partition by hash (x) partitions 2 stored as kudu;

describe formatted table_name_demo;
...
kudu.table_name  | impala::some_database.table_name_demo

使用Kudu与HiveMetastore集成

从Kudu1.10和Impala3.3开始,Impala支持与HiveMetastore(HMS)集成的Kudu服务。

以下是使用与HMS集成的Kudu服务时需要考虑的一些更改。

  • 当Kudu与HiveMetastore集成时,必须将Impala配置为使用与Kudu相同的HMS。
  • 由于Kudu表和外部表之间可能没有一对一的映射关系,因此只有内部表会自动同步。
  • 在Kudu中创建表时,Kudu会为该表创建一个具有内部表类型的HMS条目。
  • Kudu服务与HMS集成后,在没有Impala的情况下在Kudu中创建表时,会在HMS中自动创建内部表条目。要通过Impala访问这些表,请运行INVALIDATEMETADATA语句,以便Impala获取最新的元数据。

将数据加载到Kudu表中

Kudu表非常适合数据以小或中等数量连续到达的用例。要将数据导入Kudu表,请使用ImpalaINSERTUPSERT语句。该LOADDATA语句不适用于Kudu表。

由于Kudu管理自己的存储层,该存储层针对比HDFS更小的块大小进行了优化,并执行自己的内务处理以保持数据均匀分布,因此它不受“许多小文件”问题的影响,并且不需要显式重组和压缩,因为数据随时间增长。Kudu表中的分区可以指定为涵盖各种可能的数据分布,而不是为每个新的一天、一小时等硬编码一个新分区,这会导致低效、难以扩展和难以扩展使用HDFS表管理分区方案。

您对Kudu表执行ETL或批量更新的策略应考虑到DML操作的一致性限制。

MakeINSERTUPDATEUPSERT操作是幂等的:也就是说,可以多次应用并且仍然产生相同的结果。

如果批量操作由于超时或高内存使用而有超过容量限制的危险,请将其拆分为一系列较小的操作。

避免在最终结果取决于精确排序的情况下运行并发ETL操作。特别是,不要依赖INSERT...SELECT从它插入的同一个表中进行选择的语句,除非在WHERE子句中包含额外的条件以避免读取同一语句中新插入的行。

因为表之间的关系不能由Impala和Kudu强制执行,并且不能一起提交或回滚,所以不要期望多表操作的事务语义。

ImpalaDML支持Kudu表(插入、更新、删除、UPSERT)

Impala仅支持Kudu表的某些DML语句。该UPDATEDELETE语句让你可以修改捻表中的数据而无需重写大量的表中的数据。的UPSERT语句充当的组合INSERTUPDATE,将其中的主键不存在的行,并更新其中的主键确实已经存在于表中的非主键列。

INSERTKudu表的声明尊重NOTNULL主键列的唯一性和要求。

因为Impala和Kudu不支持事务,任何INSERT,UPDATE,或DELETE语句的效果都是立即可见的。例如,您不能执行一系列UPDATE语句,而只能在所有语句完成后才使更改可见。此外,如果DML语句中途失败,任何已插入、删除或更改的行仍保留在表中;没有回滚机制来撤消更改。

特别是,INSERT...SELECT引用要插入的表的语句可能会插入比预期更多的行,因为SELECT语句的一部分会看到一些新行被插入并再次处理它们。

笔记:LOADDATA语句涉及操作HDFS数据文件,不适用于Kudu表。

从Impala2.9开始,对Kudu表的INSERTorUPSERT操作会自动将交换和排序节点添加到根据目标表的分区/主键方案对行进行分区和排序的计划中(除非要插入的行数为小到足以触发单节点执行)。由于Kudu在写入时对行进行分区和排序,预分区和排序减轻了Kudu的部分负载,并帮助INSERT完成大型操作而不会超时。但是,此默认行为可能会降低INSERTUPSERT操作的端到端性能。从Impala2.10开始,您可以使用/*+NOCLUSTERED*//*+NOSHUFFLE*/在将行发送到Kudu之前,一起提示禁用分区和排序。此外,由于排序可能会消耗大量内存,请考虑MEM_LIMIT为这些查询设置查询选项。

Kudu表的一致性注意事项

Kudu表具有一致性特征,例如唯一性、由主键列和不可为空的列控制。一致性的重点是防止在表中存储重复或不完整的数据。

目前,Kudu没有对操作顺序、多行语句的完全成功或完全失败或在写入操作正在进行时读取的数据强制执行强一致性。更改以原子方式应用于每一行,但不会作为单个单元应用于受多行DML语句影响的所有行。也就是说,Kudu目前没有原子的多行语句或语句之间的隔离。

如果在DML操作期间由于与重复的主键值、NOTNULL约束等不匹配而拒绝某些行,则该语句会成功并显示警告。Impala仍会插入、删除或更新不受约束冲突影响的其他行。

因此,在Kudu表上受DML操作影响的行数可能与您预期的不同。

由于对同时在多个表中插入、删除或更新的信息没有强一致性保证,请考虑在可行的情况下对数据进行非规范化。也就是说,如果您运行单独的INSERT语句将相关行插入到两个不同的表中,一个INSERT可能会失败而另一个会成功,从而使数据处于不一致的状态。即使两次插入都成功,在第一条语句和第二条语句完成的时间间隔内可能会发生连接查询,查询会遇到不完整的不一致数据。将数据非规范化为单个宽表可以减少由于多表操作而导致不一致的可能性。

有关受DML操作影响的行数的信息在impala-shell输出和PROFILE输出中报告,但当前未报告给HiveServer2客户端,例如JDBC或ODBC应用程序。

Kudu表的安全注意事项

Kudu表的安全性包括:

游侠授权。

  • 出于以下考虑,必须向委托人授予和撤销对Kudu表的访问权限:
    • 只有拥有ALL权限的用户SERVER才能创建外部Kudu表。
    • ALL特权SERVER必须指定kudu.master_addresses在属性CREATETABLE报表管理表格,以及外部表。
    • 在表级别和列级别强制执行对Kudu表的访问。
    • SELECT-和INSERT特异性权限的支持。
    • DELETEUPDATEUPSERT操作要求ALL特权。
  • Kerberos身份验证。
  • TLS加密。
  • 血统追踪。
  • 审计。
  • 从日志文件中编辑敏感信息。

Kudu表的Impala查询性能

对于涉及Kudu表的查询,Impala可以将过滤结果集的大部分工作委托给Kudu,从而避免在包含HDFS数据文件的表的全表扫描中涉及的一些I/O。这种类型的优化对于分区Kudu表特别有效,其中Impala查询WHERE子句引用一个或多个也用作分区键列的主键列。例如,如果一个分区的Kudu表使用了一个HASH子句forcol1和一个RANGE子句forcol2,那么使用诸如此类的子句的查询WHEREcol1IN(1,2,3)ANDcol2>100可以准确地确定哪些平板服务器包含相关数据,从而非常有效地并行化查询。

在Impala2.11及更高版本中,Impala可以下推附加信息以优化涉及Kudu表的连接查询。如果连接子句包含形式的谓词,则在Impala为更大表(HDFS表或Kudu表)中的连接列构建可能匹配值的哈希表后,Impala可以“下推”最小和最大匹配列值到Kudu,以便Kudu可以更有效地定位第二个(较小)表中的匹配行。这些最小/最大滤波器受影响,以及查询选项;最小/最大过滤器不受,,column=expressionRUNTIME_FILTER_MODERUNTIME_FILTER_WAIT_TIME_MSDISABLE_ROW_RUNTIME_FILTERINGRUNTIME_BLOOM_FILTER_SIZERUNTIME_FILTER_MIN_SIZERUNTIME_FILTER_MAX_SIZE,和MAX_NUM_RUNTIME_FILTERS查询选项。

该语句的TABLESAMPLE子句SELECT不适用于从视图、子查询或真实基表以外的任何内容派生的表引用。该子句仅适用于由HDFS或类似HDFS的数据文件支持的表,因此不适用于Kudu或HBase表。