您现在的位置是:首页 >其他 >【Trino实战】Hive connector功能性文档网站首页其他

【Trino实战】Hive connector功能性文档

顧棟 2024-07-13 12:01:02
简介【Trino实战】Hive connector功能性文档

Hive connector功能性文档


Trino需要使用Hive下的 元数据服务(HMS)和对象存储系统(HDFS),协调器和所有的工作机需要和他们是保证网络畅通。

该连接器提供对配置的对象存储系统和元数据存储中的数据和元数据的读取访问和写入访问:

  • 全局可用语句
  • 读操作
  • 写操作
  • 安全操作
  • 事务

SQL语言上的支持

基础使用案例

创建内部表

在catalog为hive下名为testdb的schema中创建一个名为page_views的Hive表,该表使用ORC文件格式存储,按ds和country进行分区,并按用户分成50个桶。注意,Hive要求分区列是表中的最后一列:

CREATE TABLE hive.testdb.page_views (
  view_time timestamp,
  user_id bigint,
  page_url varchar,
  ds date,
  country varchar
)
WITH (
  format = 'ORC',
  partitioned_by = ARRAY['ds', 'country'],
  bucketed_by = ARRAY['user_id'],
  bucket_count = 50
)

创建库

创建一个新的名为web的Hive schema,将表存储在S3下一个名为my-bucket的路径中,with用来指定location。

CREATE SCHEMA hive.web
WITH (location = 's3://my-bucket/')

删除库

删除schema

DROP SCHEMA example.web

新增分区

CALL hive.system.create_empty_partition(
    schema_name => 'testdb',
    table_name => 'page_views',
    partition_columns => ARRAY['ds', 'country'],
    partition_values => ARRAY['2023-05-26', 'CN']);

查询表中的分区列表

SELECT * FROM hive.testdb."page_views$partitions"

删除分区

删除表中的一个分区

DELETE FROM hive.testdb.page_views
WHERE ds = DATE '2023-05-26'
  AND country = 'CN'

全表查询

SELECT * FROM hive.testdb.page_views

创建外部表

创建一个名为request_logs的外部Hive表,指向S3中的现有数据:

CREATE TABLE example.web.request_logs (
  request_time timestamp,
  url varchar,
  ip varchar,
  user_agent varchar
)
WITH (
  format = 'TEXTFILE',
  external_location = 's3://my-bucket/data/logs/'
)

分析表

收集request_logs表的统计数据:

ANALYZE hive.testdb.page_views
  
ANALYZE hive.testdb.page_views WITH (partitions = ARRAY[ARRAY['2023-05-26','CN']]);

删除外部表

删除外部表 request_logs。这只是删除了该表的元数据。被引用的数据目录不会被删除:

DROP TABLE example.web.request_logs

创建事务表

CREATE TABLE AS可以用来创建ORC格式的事务表,像这样:

CREATE TABLE <name>
WITH (
    format='ORC',
    transactional=true
)
AS <query>

删除page_views表的一个分区的统计信息

删除page_views表的一个分区的统计信息:

CALL hive.system.drop_stats(
    schema_name => 'testdb',
    table_name => 'page_views',
    partition_values => ARRAY[ARRAY['2023-05-26', 'CN']]);

程序

使用CALL语句来执行数据操作或管理任务。如果你的Hive catalog被称为web,那么程序必须包括一个合格的catalog名称:

CALL web.system.example_procedure()

有以下程序可供选择:

  • system.create_empty_partition(schema_name, table_name, partition_columns, partition_values):在指定的表中创建一个空分区。

  • system.sync_partition_metadata(schema_name, table_name, mode, case_sensitive):检查和更新元存储中的分区列表。有三种模式可供选择:

    • ADD:添加任何存在于文件系统中,但不在元存储中的分区。
    • DROP:丢弃任何存在于元存储中,但不存在于文件系统中的分区。
    • FULL:同时执行ADD和DROP。

    参数case_sensitive是可选的。为了与Hive的MSCK REPAIR TABLE行为兼容,默认值为true,它希望文件系统路径中的分区列名使用小写(例如col_x=SomeValue)。文件系统中不符合这一惯例的分区会被忽略,除非该参数被设置为false

  • system.drop_stats(schema_name, table_name, partition_values):丢弃一个分区子集或整个表的统计数据。分区被指定为一个数组,其元素是分区值的数组(类似于create_empty_partition中的partition_values参数)。如果省略了partition_values参数,则会放弃整个表的统计信息。

  • system.register_partition(schema_name, table_name, partition_columns, partition_values, location)

    将现有的位置注册为指定表的元存储中的一个新分区。

    当省略location参数时,分区位置是使用partition_columnspartition_values构建的。

    由于安全原因,只有当hive.allow-register-partition-procedure被设置为 true 时,该程序才会被启用。

  • system.unregister_partition(schema_name, table_name, partition_columns, partition_values):在元存储中为指定的表取消注册现有的分区。分区数据不被删除。

  • system.flush_metadata_cache():刷新所有的Hive元数据缓存。

  • system.flush_metadata_cache(schema_name => ..., table_name => ...):刷新与选定表相关的Hive元数据缓存条目。该过程需要传递命名的参数

  • system.flush_metadata_cache(schema_name => ..., table_name => ..., partition_columns => ARRAY[...], partition_values => ARRAY[...]):刷新与选定分区相关的Hive元数据缓存条目。该过程需要传递命名参数。

数据管理

一些数据管理语句可能会受到Hive目录的授权检查策略的影响。在默认的传统策略中,一些语句被默认禁用。更多信息请参见Hive连接器安全配置。

数据管理功能包括对INSERTUPDATEDELETEMERGE语句的支持,具体支持情况取决于存储系统、文件格式和元存储。

当连接到3.x版本的Hive元存储时,Hive连接器支持从只写表ACID表中读取和写入,并完全支持分区和分桶。

DELETE:只有当表被分区且WHERE子句匹配整个分区时,才支持应用于非事务表的DELETE。具有ORC格式的事务型Hive表支持 "逐行 "删除,其中的WHERE子句可以匹配任意的行集。

UPDATE:只有格式为ORC的事务性Hive表才支持UPDATE。不支持分区或桶列的UPDATE。

MERGE:只支持ACID表。

不支持使用Hive Streaming Ingest(Hive 3)创建的ACID表。

Schema与表管理

Hive连接器支持查询和操作Hive表和模式(数据库)。虽然有些不常见的操作必须直接使用Hive进行,但大多数操作都可以使用Trino进行。

模式演化

Hive允许一个表中的分区拥有与表不同的模式。当一个表的列类型在分区已经存在(使用原来的列类型)之后被改变时,就会出现这种情况。Hive连接器通过允许与Hive相同的转换来支持这种情况:

  • varchartinyintsmallintintegerbigint之间的转换

  • realtodouble

  • 整数的扩展转换,比如tinyintsmallint

任何转换失败的结果都是null,这与Hive的行为相同。例如,将字符串’foo’转换为数字,或者将字符串’1234’转换为tinyint(其最大值为127)。

Avro模式的演变

Trino支持查询和操作具有Avro存储格式的Hive表,它的模式设置是基于Avro模式file/literal。Trino也能够通过从位于本地或远程HDFS/Web服务器中的有效Avro模式文件推断出模式,从而在Trino中创建表。

要指定Avro模式应该被用来解释表数据,请使用avro_schema_url表属性。

模式可以放在本地文件系统中,也可以远程放在以下位置:

  • HDFS(例如,avro_schema_url = 'hdfs://user/avro/schema/avro_data.avsc')

  • S3(例如,avro_schema_url = 's3n:///schema_bucket/schema/avro_data.avsc')

  • 一个网络服务器(例如,avro_schema_url = 'http://example.org/schema/avro_data.avsc')。

模式所在的URL必须能够从Hive元存储和Trino协调器/工作节点中访问。

另外,你也可以使用表属性avro_schema_literal来定义Avro模式。

在Trino中使用avro_schema_urlavro_schema_literal属性创建的表与设置了avro.schema.urlavro.schema.literal的Hive表的行为方式相同。

例子:

CREATE TABLE example.avro.avro_data (
   id bigint
 )
WITH (
   format = 'AVRO',
   avro_schema_url = '/usr/local/avro_data.avsc'
)

如果指定了avro_schema_url,那么在DDL中列出的列(上面例子中的id)将被忽略。表的模式与Avro模式文件中的模式相匹配。在任何读取操作之前,Avro模式被访问,所以查询结果反映了模式的任何变化。因此,Trino利用了Avro的后向兼容能力。

如果表的模式在Avro模式文件中发生变化,新的模式仍然可以用来读取旧的数据。新添加/重命名的字段在Avro模式文件中必须有一个默认值。

架构演变行为如下:

  • 在新模式中添加的列: 当表使用新模式时,用旧模式创建的数据产生一个默认值。

  • 在新模式中删除列: 用旧模式创建的数据不再输出被移除的列的数据。

  • 列在新模式中被重新命名: 这相当于删除列并添加一个新的列,当表使用新模式时,用旧模式创建的数据产生一个默认值。

  • 在新的模式中改变列的类型: 如果Avro或Hive连接器支持类型强制,那么就会进行转换。对于不兼容的类型,会产生一个错误。

限制条件

avro_schema_url被设置时,以下操作不被支持:

  • 不支持CREATE TABLE AS
  • CREATE TABLE中不支持Bucketing(bucketed_by)列。
  • 不支持修改列的ALTER TABLE命令。

ALTER TABLE EXECUTE

连接器支持与ALTER TABLE EXECUTE一起使用的优化命令。

优化命令用于重写指定的非事务表的内容,以便将其合并为更少但更大的文件。如果表是分区的,数据压缩将分别作用于为优化而选择的每个分区。这个操作提高了读取性能。

所有大小低于可选的file_size_threshold参数(阈值的默认值是100MB)的文件被合并:

ALTER TABLE test_table EXECUTE optimize

下面的语句合并了表内大小在10兆字节以下的文件:

ALTER TABLE test_table EXECUTE optimize(file_size_threshold => '10MB')

你可以使用一个WHERE子句,用用于分区表的列,来过滤哪些分区被优化:

ALTER TABLE test_partitioned_table EXECUTE optimize
WHERE partition_key = 1

优化命令默认是禁用的,可以通过<catalog-name>.non_transactional_optimize_enabled会话属性为一个目录启用:

SET SESSION <catalog_name>.non_transactional_optimize_enabled=true

由于Hive表是非事务性的,请注意以下可能的结果:

如果对当前正在优化的表运行查询,可能会读取重复的行。

在极少数情况下,在optimize操作过程中出现异常,需要手动清理表的目录。在这种情况下,参考Trino日志和查询失败信息,看看哪些文件必须被删除。

表属性

表属性为底层表提供或设置元数据。这是CREATE TABLE AS语句的关键。表的属性是通过WITH子句传递给连接器的:

CREATE TABLE tablename
WITH (format='CSV',
      csv_escape = '"')

Hive connector table properties

Property nameDescriptionDefault
auto_purge指示配置的元存储在删除表或分区时执行清除,而不是使用回收站进行软删除。
avro_schema_url指向表的Avro schema evolution的URI。
bucket_count数据分组的桶的数量。只在与bucketed_by一起使用时有效。0
bucketed_by存储表的bucketing列。只在与bucket_count一起使用时有效。[]
bucketing_version指定使用哪一个Hive bucketing版本。有效值是12
csv_escapeCSV转义字符. Requires CSV format.
csv_quoteCSV引号字符. Requires CSV format.
csv_separatorCSV的分隔符。要求是CSV格式。你可以使用其他分隔符,如`,或使用Unicode配置隐形分隔符,如用U&‘009’`的标签。
external_locationS3、Azure Blob存储等的外部Hive表的URI。 See the Basic usage examples for more information.
format表的文件格式。有效值包括ORC, PARQUET, AVRO, RCBINARY, RCTEXT, SEQUENCEFILE, JSON, TEXTFILE, CSV, 和 REGEX。目录属性hive.storage-format设置了默认值,可以将其改为不同的默认值。
null_format`NULL’值的序列化格式。需要TextFile, RCText, 或SequenceFile格式。
orc_bloom_filter_columns逗号分隔的列列表,用于ORC bloom过滤器。它提高了读取ORC文件时使用范围谓词的查询性能。Requires ORC format.[]
orc_bloom_filter_fppThe ORC bloom filters false positive probability. Requires ORC format.0.05
partitioned_by存储表的分区列。在partitioned_by子句中列出的列必须是DDL中定义的最后一列。[]
skip_footer_line_count在解析文件的数据时要忽略的页脚行数。 Requires TextFile or CSV format tables.
skip_header_line_count在解析文件的数据时要忽略的标题行数。 Requires TextFile or CSV format tables.
sorted_by要排序的列,以确定行的bucketing。只有在指定了bucketed_bybucket_count时才有效。[]
textfile_field_separator允许对TextFile格式的表格使用自定义字段分隔符,如’|'。
textfile_field_separator_escape允许对TextFile格式的表格使用自定义转义字符。
transactional将此属性设置为true以创建一个ORC ACID事务表。Requires ORC format. 对于使用旧版本的Hive创建的仅供插入的表,该属性可能显示为true。
partition_projection_enabled启用所选表的分区投影。从AWS Athena表属性projection.enabled映射过来。
partition_projection_ignore忽略任何存储在元存储中的选定表的分区投影属性。这是一个Trino专用的属性,允许你在特定的表上解决兼容性问题,如果启用,Trino会忽略所有其他与分区投影有关的配置选项。
partition_projection_location_template预测的分区位置模板,如s3a://test/name=${name}/。从AWS Athena表属性storage.location.template中映射出来${table_location}/${partition_name}
extra_properties添加到Hive表中的额外属性。这些属性不被Trino使用,可以在$properties元数据表中找到。这些属性不包括在SHOW CREATE TABLE语句的输出中。

Metadata tables

原始的Hive表属性可以作为一个隐藏的表,每个表属性包含一个单独的列,单行包含属性值。属性表的名称与附加了$properties的表的名称相同。

你可以用一个简单的查询来检查属性名称和值:

SELECT * FROM hive.testdb."page_views$properties";

Column properties

Hive connector column properties

Property nameDescriptionDefault
partition_projection_type定义在此列上使用的分区投影的类型。只可以在分区列上使用。可用的类型: enum, integer, date, injected. 从AWS Athena表属性projection.${columnName}.type映射过来。
partition_projection_valuespartition_projection_type设置为ENUM一起使用。包含一个用于生成分区的静态值列表。从AWS Athena表属性projection.${columnName}.values映射过来。
partition_projection_rangepartition_projection_type设置为INTEGERDATE一起使用,定义一个范围。它是一个双元素数组,描述了用于生成分区的最小和最大范围值。生成从最小值开始,然后通过定义的partition_projection_interval递增到最大值。例如,对于 “partition_projection_type “的 “INTEGER”,格式是”[‘1’, ‘4’]”;对于 “partition_projection_type “的 “DATE”,格式是”[‘2001-01-01’, ‘2001-01-07’]“或”`NOW-3DAYS’, ‘NOW’”。从AWS Athena表属性projection.${columnName}.range映射出来。
partition_projection_intervalpartition_projection_type设置为INTEGERDATE一起使用。它表示用于在给定范围partition_projection_range内生成分区的时间间隔。从AWS Athena表属性projection.${columnName}.interval映射过来。
partition_projection_digitspartition_projection_type设置为INTEGER一起使用。整数列投影要使用的数字。从AWS Athena表属性projection.${columnName}.digits映射过来。
partition_projection_formatpartition_projection_type设置为DATE时使用。日期列的投影格式,定义为字符串,如yyyy MMMM-dd-yy HH:mm:ss,用于Java DateTimeFormatter类。从AWS Athena表属性projection.${columnName}.format映射过来。
partition_projection_interval_unitpartition_projection_type=DATA一起使用。partition_projection_interval中给出的日期列投影范围间隔单位。从AWS Athena表属性projection.${columnName}.interval.unit映射过来。

Metadata columns

除了定义的列之外,Hive连接器还在每个表中的一些隐藏列中主动展示元数据:

  • $bucket: 该行的桶号

  • $path: 该行的文件的完整文件系统路径名称

  • $file_modified_time: 这一行的文件最后修改的日期和时间

  • $file_size: 这一行的文件大小

  • $partition: 此行的分区名称

你可以像其他列一样在你的SQL语句中使用这些列。它们可以被直接选择,或在条件语句中使用。例如,你可以检查每条记录的文件大小、位置和分区:

SELECT *, "$path", "$file_size", "$partition"
FROM hive.testdb.page_views;

检索属于存储在分区ds=2016-08-09/country=US中的文件的所有记录:

SELECT *, "$path", "$file_size"
FROM hive.testdb.page_views
WHERE "$partition" = 'ds=2023-05-26/country=CN'

视图管理

Trino允许从Hive物化视图中读取数据,并且可以配置为支持读取Hive视图。

物化视图

Hive连接器支持从Hive物化视图中读取数据。在Trino中,这些视图是作为普通的、只读的表呈现的。

Hive视图

Hive视图在HiveQL中定义,并存储在Hive Metastore服务中。它们被分析为允许对数据进行读取访问。

Hive连接器包括支持以三种不同的模式来读取Hive视图。

  • Disabled
  • Legacy
  • Experimental

可以在目录属性文件中配置该行为。

默认情况下,Hive视图是以RUN AS DEFINER安全模式执行的。将hive.hive-views.run-as-invoker目录配置属性设置为true,以使用RUN AS INVOKER语义。

Disabled

默认行为是忽略Hive视图。这意味着你在视图中编码的业务逻辑和数据在Trino中是不可用的。

Legacy

可以用hive.hive-views.enabled=truehive.hive-views.legacy-translation=true启用一个非常简单的实现来执行Hive视图,从而允许在Trino中读取数据。

对于临时使用特定目录的遗留行为,你可以将hive_views_legacy_translation目录会话属性设置为true

该传统行为将任何定义视图的HiveQL查询解释为用SQL编写。它不做任何翻译,而是依赖于HiveQL与SQL非常相似的事实。

这对于非常简单的Hive视图来说是可行的,但对于更复杂的查询来说可能会导致问题。例如,如果一个HiveQL函数有相同的签名,但与SQL版本有不同的行为,那么返回的结果可能会有所不同。在更极端的情况下,查询可能会失败,甚至不能被解析和执行。

Experimental

新的行为被设计得更好,并有可能变得比传统的实现更强大。它可以分析、处理和重写Hive视图以及包含的表达式和语句。

它支持以下Hive视图功能:

  • 针对Hive视图的UNION [DISTINCT] 和 UNION ALL

  • 嵌套的GROUP BY条款

  • current_user()

  • LATERAL VIEW OUTER EXPLODE

  • LATERAL VIEW [OUTER] EXPLODE on array of struct

  • LATERAL VIEW json_tuple

你可以用hive.hive-views.enabled=true来启用实验性行为。删除hive.hive-views.legacy-translation属性或将其设置为false以确保legacy不被启用。

在试验这一功能时,请记住,许多功能尚未实现。以下是一个不完整的缺失功能列表:

  • HiveQL的current_date、current_timestamp及其他

  • Hive函数调用,包括translate()、窗口函数等

  • 常见的表格表达式和简单的案例表达式

  • 时间戳的精度设置

  • 支持所有的Hive数据类型以及与Trino类型的正确映射

  • 能够处理自定义的UDFs

Trino SQL与HiveQL上的不同

Trino使用ANSI SQL的语法和语义,而Hive使用一种类似于SQL的语言,称为HiveQL,它是松散地仿照MySQL(它本身与ANSI SQL有许多不同之处)。

使用下标来访问一个数组的动态索引,而不是使用udf

SQL中的下标运算符支持完整的表达式,而不像Hive(只支持常量)。因此,你可以写这样的查询:

SELECT my_array[CARDINALITY(my_array)] as last_element
FROM ...

避免数组的越界访问

访问数组中的界外元素将导致一个异常。你可以用一个if来避免这种情况,如下所示

SELECT IF(CARDINALITY(my_array) >= 3, my_array[3], NULL)
FROM ...

对数组使用ANSI SQL语法

数组的索引是从1开始的,而不是从0开始

SELECT my_array[1] AS first_element
FROM ...

用ANSI语法构造数组:

SELECT ARRAY[1, 2, 3] AS my_array

对标识符和字符串使用ANSI SQL语法

字符串用单引号分隔,标识符用双引号引出,而不是反引号:

SELECT name AS "User Name"
FROM "7day_active"
WHERE name = 'foo'

以数字开头的报价标识符

在ANSI SQL中,以数字开头的标识符是不合法的,必须使用双引号进行引用:

SELECT *
FROM "7day_active"

使用标准的字符串连接操作符

使用ANSI SQL的字符串连接操作符:

SELECT a || b || c
FROM ...

对CAST目标使用标准类型

CAST目标支持以下标准类型:

SELECT
  CAST(x AS varchar)
, CAST(x AS bigint)
, CAST(x AS double)
, CAST(x AS boolean)
FROM ...

特别是,使用VARCHAR而不是STRING。

整数除法时使用 CAST

Trino在除以两个整数时遵循执行整数除法的标准行为。例如,7除以2的结果是3,而不是3.5。要对两个整数进行浮点除法,应将其中一个整数投给一个双数:

SELECT CAST(5 AS DOUBLE) / 2

对复杂的表达式或查询使用 WITH

当你想重新使用一个复杂的输出表达式作为过滤器时,可以使用内联子查询或使用WITH子句将其分解:

WITH a AS (
  SELECT substr(name, 1, 3) x
  FROM ...
)
SELECT *
FROM a
WHERE x = 'foo'

使用UNNEST来扩展数组和集合

Trino支持UNNEST来扩展数组和集合。使用UNNEST而不是LATERAL VIEW explode()。

Hive query:

SELECT student, score
FROM tests
LATERAL VIEW explode(scores) t AS score;

Trino query:

SELECT student, score
FROM tests
CROSS JOIN UNNEST(scores) AS t (score);

对日期和时间的INTERVAL表达式使用ANSI SQL语法

Trino支持ANSI SQL风格的INTERVAL表达式,与Hive中使用的实现方式不同。

  • INTERVAL关键字是必须的,不是可有可无的。

  • 日期和时间单位必须是单数。例如day,而不是days。

  • 值必须有引号。

Hive查询:

SELECT cast('2000-08-19' as date) + 14 days;

等效的Trino查询:

SELECT cast('2000-08-19' as date) + INTERVAL '14' day

使用 datediff 的注意点

Hive datediff函数返回两个日期之间的天数差,其声明如下:

datediff(string enddate, string startdate) -> integer

相等的Trino函数date_diff对两个日期参数使用相反的顺序,并且需要一个单位。在迁移时必须考虑到这一点:

Hive查询:

datediff(enddate, startdate)

Trino查询:

date_diff('day', startdate, enddate)

在插入时覆盖数据

默认情况下,INSERT查询是不允许覆盖现有数据的。你可以使用目录会话属性insert_existing_partitions_behavior来允许覆写。在使用Hive连接器的目录名称前加上前缀,例如hdfs,并在运行插入查询之前在会话中设置该属性:

SET SESSION hdfs.insert_existing_partitions_behavior = 'OVERWRITE'INSERT INTO hdfs.schema.table ...

产生的行为等同于在Hive中使用INSERT OVERWRITE。

当表存储在加密的HDFS上,当表是未分区的或者表是事务性的,Trino不支持插入覆盖操作。


支持容错执行

该连接器支持查询处理的容错执行。在非事务表上的任何重试策略下都支持读和写操作。

读取操作在事务表上支持任何重试策略。写操作和CREATE TABLE … AS操作不支持在事务表上的任何重试策略。可以通过部署配置启动外部数据缓存加强容错执行。


性能

表的统计信息

Hive连接器支持收集和管理表的统计数据以提高查询处理性能。

在写入数据时,Hive连接器总是收集基本的统计数据(numFiles, numRows, rawDataSize, totalSize),默认情况下也会收集列级的统计数据:

Column typeCollectible statistics
TINYINT空值的数量,不同值的数量,最小/最大值
SMALLINT空值的数量,不同值的数量,最小/最大值
INTEGER空值的数量,不同值的数量,最小/最大值
BIGINT空值的数量,不同值的数量,最小/最大值
DOUBLE空值的数量,不同值的数量,最小/最大值
REAL空值的数量,不同值的数量,最小/最大值
DECIMAL空值的数量,不同值的数量,最小/最大值
DATE空值的数量,不同值的数量,最小/最大值
TIMESTAMP空值的数量,不同值的数量,最小/最大值
VARCHAR空值的数量,不同值的数量,
CHAR空值的数量,不同值的数量,
VARBINARY空值的数量
BOOLEAN空值的数量,True/False值的数量,

更新表和分区的统计数据

如果你的查询很复杂,包括连接大型数据集,在表/分区上运行ANALYZE可能会通过收集数据的统计信息来提高查询性能。

当分析一个分区表时,可以通过可选的分区属性来指定要分析的分区,分区属性是一个数组,包含分区键的值,其顺序是在表模式中声明的:

ANALYZE table_name WITH (
    partitions = ARRAY[
        ARRAY['p1_value1', 'p1_value2'],
        ARRAY['p2_value1', 'p2_value2']])

这个查询将收集两个分区的统计数据,键值为p1_value1, p1_value2和p2_value1, p2_value2。

在宽表上,收集所有列的统计数据可能很昂贵,并对查询计划产生不利影响。它通常也是不必要的–统计数据只对特定的列有用,比如连接键、谓词、分组键。我们可以通过可选的列属性指定要分析的列的子集:

ANALYZE table_name WITH (
    partitions = ARRAY[ARRAY['p2_value1', 'p2_value2']],
    columns = ARRAY['col_1', 'col_2'])

这个查询收集了键值为p2_value1, p2_value2的分区的col_1和col_2列的统计信息。

请注意,如果以前收集了所有列的统计数据,那么在重新分析一个子集之前,必须放弃它们:

CALL system.drop_stats('schema_name', 'table_name')

你也可以只删除选定分区的统计数据:

CALL system.drop_stats(
    schema_name => 'schema',
    table_name => 'table',
    partition_values => ARRAY[ARRAY['p2_value1', 'p2_value2']])

动态过滤

Hive连接器支持动态过滤优化。对于以任何文件格式存储的分区表,支持动态分区修剪,用于广播和分区连接。对于以任何文件格式存储的桶状表,仅支持广播连接的动态桶状修剪。

对于以ORC或Parquet文件格式存储的表,动态过滤器也被推送到工作节点的本地表扫描中,用于广播连接。推送到ORC和Parquet读取器中的动态过滤器谓词被用来执行条带或行组修剪并节省磁盘I/O。在ORC或Parquet文件中按连接标准中使用的列对数据进行排序,可以显著提高条带或行组修剪的有效性。这是因为在同一条带或行组内对类似的数据进行分组,可以极大地提高在条带或行组级别维护的最小/最大索引的选择性。

延迟执行动态过滤器

在开始表扫描之前,等待动态过滤器的收集往往是有益的。如果动态过滤能够减少扫描的数据量,那么这种额外的等待时间可能会在查询和CPU时间上带来巨大的节省。

对于Hive连接器,通过使用目录文件中的配置属性hive.dynamic-filtering.wait-timeout或目录会话属性<hive-catalog>.dynamic_filtering_wait_timeout,可以将表扫描延迟到收集动态过滤器的时间。

存储缓存

使用Hive连接器查询对象存储是Trino的一个非常常见的用例。它经常涉及到大量数据的传输。对象是由多个工作者从HDFS或任何其他支持的对象存储中检索出来的,并在这些工作者上进行处理。带有不同参数的重复查询,甚至是来自不同用户的不同查询,经常会访问,并因此传输相同的对象。

好处

  • 降低对象存储的负荷

    每一个被检索和缓存的对象都避免了在后续查询中从存储中重复检索。因此,对象存储系统不必一次又一次地提供该对象。

    例如,如果你的查询从存储中访问100MB的对象,在查询第一次运行时,100MB被下载和缓存。任何后续查询都会使用这些对象。如果你的用户再运行100个查询,访问相同的对象,你的存储系统就不必做任何重要的工作。如果没有缓存,它必须一次又一次地提供相同的对象,导致10GB的总存储服务。

    这种对对象存储的负载减少也会影响对象存储系统的大小,从而影响其成本。

  • 提高查询性能

    缓存可以提供显著的性能优势,因为它避免了重复的网络传输,而是从本地缓存中访问对象的副本。如果直接访问对象存储的性能比访问缓存的性能低,那么性能提升就更加显著。

    例如,如果你在不同的网络、不同的数据中心、甚至不同的云提供商区域访问对象存储,查询性能就会很慢。增加使用快速的本地存储的缓存有很大的影响,使你的查询速度大大加快。

    另一方面,如果你的对象存储已经在I/O和网络访问方面以非常高的性能运行,而你的本地缓存存储的速度类似,甚至更慢,那么性能方面的好处可能是最小的。

  • 降低查询成本

    前面提到的减少对象存储的负载的结果是大大减少了网络流量。然而,网络流量在设置中是一个相当大的成本因素,特别是在公共云供应商系统中托管时。

架构

缓存可以在两种模式下运行。异步模式直接提供被查询的数据,并在之后异步地缓存任何对象。异步是默认和推荐的模式。查询不需要支付预热缓存的费用。缓存是在后台填充的,如果缓存还没有填充,查询就会绕过缓存。任何请求缓存对象的后续查询都会直接从缓存中得到服务。

另一种模式是缓存回调。在这种模式下,如果一个对象在缓存中没有找到,它将从存储中读取,放在缓存中,然后提供给请求的查询。在通读模式下,查询总是从高速缓存中读取,并且必须等待高速缓存的填充。

在这两种模式下,对象都被缓存在每个工作者的本地存储中。工作者可以从其他工作者那里请求缓存的对象,以避免从对象存储中请求。

缓存块的大小为1MB,很适合ORC或Parquet文件格式。

配置

缓存功能是Hive连接器的一部分,可以在目录属性文件中激活:

connector.name=hive

hive.cache.enabled=true
hive.cache.location=/opt/hive-cache

缓存在协调器和所有访问对象存储的工作者上运行。用于管理BookKeeper和数据传输的网络端口(默认为8898和8899)需要是可用的。

要在多个目录上使用缓存,你需要配置不同的缓存目录和不同的BookKeeper和数据传输端口。

Cache Configuration Parameters

PropertyDescriptionDefault
hive.cache.enabled切换以启用或禁用缓存false
hive.cache.location用于每个工作者的缓存存储的必要目录位置。用逗号分隔多个目录,这些目录可以是单独驱动器的挂载点。更多提示可以在 recommendation 中找到。例如:hive.cache.location=/var/lib/trino/cache1,/var/lib/trino/cache2
hive.cache.data-transfer-port用于传输缓存管理的数据的TCP/IP端口。8898
hive.cache.bookkeeper-port管理缓存的BookKeeper使用的TCP/IP端口。8899
hive.cache.read-mode缓存的操作模式,如前面架构部分所述。支持的模式是 "async"和 “read-through”。async
hive.cache.ttl缓存中的对象的保存时长。没有被请求过TTL值的对象会从缓存中删除。7d
hive.cache.disk-usage-percentage用于缓存数据的磁盘空间的百分比。80

建议

本地缓存存储的速度对缓存的性能至关重要。最常见和最经济的方法是安装高性能的SSD磁盘或类似的磁盘。快速的缓存性能也可以用RAM磁盘作为内存来实现。

在所有情况下,你应该避免使用节点的根分区和磁盘,而是在每个节点上为缓存附加多个专用的存储设备。缓存使用磁盘的比例是可配置的。存储应该是每个协调者和工作者节点上的本地存储。在Trino启动之前,目录需要存在。我们建议使用多个设备来提高缓存的性能。

连接的存储设备的容量应该比查询的对象存储工作负载的大小大20-30%左右。例如,你目前的查询工作负载通常访问HDFS存储中的分区,这些分区封装了过去3个月的数据。这些分区的总体大小目前为1TB。因此,你的缓存驱动器的总容量必须是1.2TB或更大。

你对Trino的部署方法决定了如何创建用于缓存的目录。通常情况下,你需要连接一个快速的存储系统,如SSD驱动器,并确保它安装在配置的路径上。Kubernetes、CFT和其他系统允许通过卷来实现。

Metrics

为了验证缓存在你的系统上是如何工作的,你可以采取多种方法:

  • 检查所有节点上的缓存存储驱动器的磁盘使用情况

  • 查询由JMX公开的缓存系统的指标

缓存的实现通过JMX暴露了一些指标。你可以直接在Trino中用JMX连接器或在外部工具中检查这些和其他指标。

目录的基本缓存统计数据可在jmx.current."rubix:catalog=<catalog_name>,name=stats "表中找到。jmx.current."rubix:catalog=<catalog_name>,type=detailed,name=stats"表包含更详细的统计数据。

下面的示例查询返回Hive目录的平均缓存命中率:

SELECT avg(cache_hit)
FROM jmx.current. "rubix:catalog=hive,name=stats"
WHERE NOT is_nan(cache_hit);

限制

缓存不支持用户模拟,不能与Kerberos保护的HDFS一起使用。它不考虑任何特定于用户的对象存储访问权限。缓存的对象对于缓存系统来说只是透明的二进制blob,并且可以完全访问所有内容。

表的重定向

Trino提供了一种可能性,即根据表的格式和目录配置,将对现有表的操作透明地重定向到适当的目录。

在依赖元存储服务的连接器的背景下(例如,Hive连接器、Iceberg连接器和Delta Lake连接器),元存储(Hive元存储服务、AWS Glue数据目录)可以用来习惯不同的表格式。因此,一个元存储数据库可以容纳各种不同格式的表。

作为一个具体的例子,让我们使用下面这个简单的场景,它利用了表的重定向:

USE example.example_schema;

EXPLAIN SELECT * FROM example_table;
                               Query Plan
-------------------------------------------------------------------------
Fragment 0 [SOURCE]
     ...
     Output[columnNames = [...]]...
     └─ TableScan[table = another_catalog:example_schema:example_table]
            ...

EXPLAIN语句的输出指出了处理表example_table的SELECT查询的实际目录。

当使用全路径表名时,表的重定向功能也能发挥作用:

EXPLAIN SELECT * FROM example.example_schema.example_table;
                               Query Plan
-------------------------------------------------------------------------
Fragment 0 [SOURCE]
     ...
     Output[columnNames = [...]]...
     └─ TableScan[table = another_catalog:example_schema:example_table]
            ...

Trino为以下操作提供表的重定向支持:

Trino不提供视图重定向支持。

该连接器支持从Hive表重定向到Iceberg和Delta Lake表,目录配置属性如下:

  • hive.iceberg-catalog-name用于将查询重定向至Iceberg连接器

  • hive.delta-lake-catalog-name用于重定向查询到Delta Lake连接器。

性能调优配置属性

下表描述了Hive连接器的性能调优属性。

性能调整配置属性被认为是专家级的功能。改变这些属性的默认值可能会导致不稳定和性能下降。

Property nameDescriptionDefault value
hive.max-outstanding-splits在调度器尝试暂停之前,查询中每个表扫描的缓冲分割的目标数量。1000
hive.max-outstanding-splits-size在查询失败之前,查询中每个表扫描的缓冲分割所允许的最大尺寸。256 MB
hive.max-splits-per-second每秒钟每张表扫描产生的最大分片数。这可以用来减少存储系统的负载。默认情况下,没有限制,这将导致Trino最大限度地实现数据访问的并行化。
hive.max-initial-splits对于每个表的扫描,协调器首先分配最多为max-initial-split-size的文件部分。在 max-initial-splits被分配后,max-split-size 被用于剩余的分片。200
hive.max-initial-split-sizemax-initial-splits分配完毕之前,分配给工作者的单个文件段的大小。较小的分割导致更多的并行性,这对较小的查询有促进作用。32 MB
hive.max-split-size分配给工作者的单个文件部分的最大尺寸。较小的分割导致更多的并行性,因此可以减少延迟,但也有更多的开销,增加系统的负载。

File formats

Hive连接器支持以下文件类型和格式:

  • ORC
  • Parquet
  • Avro
  • RCText (RCFile using ColumnarSerDe)
  • RCBinary (RCFile using LazyBinaryColumnarSerDe)
  • SequenceFile
  • JSON (using org.apache.hive.hcatalog.data.JsonSerDe)
  • CSV (using org.apache.hadoop.hive.serde2.OpenCSVSerde)
  • TextFile

ORC format configuration properties

以下属性用于配置由Hive连接器执行的对ORC文件的读和写操作。

Property NameDescriptionDefault
hive.orc.time-zone为没有声明时区的传统ORC文件设置默认时区。JVM default
hive.orc.use-column-names通过名称访问ORC列。默认情况下,ORC文件中的列是通过它们在Hive表定义中的序号位置来访问的。相等的目录会话属性是orc_use_column_namesfalse
hive.orc.bloom-filters.enabled启用用于谓词下推的Bloom过滤器。false

Parquet format configuration properties

以下属性用于配置由Hive连接器执行的对Parquet文件的读写操作。

Property NameDescriptionDefault
hive.parquet.time-zone将时间戳值调整到一个特定的时区。对于Hive 3.1+,将其设置为UTC。JVM default
hive.parquet.use-column-names默认情况下,通过名称访问Parquet列。将此属性设置为false,以按Hive表定义中的顺序位置访问列。相等的目录会话属性是parquet_use_column_namestrue
parquet.optimized-reader.enabled读取Parquet文件时,是否使用分批读列器以提高性能。将此属性设置为false以默认禁用优化的parquet阅读器。相等的目录会话属性是parquet_optimized_reader_enabledtrue
parquet.optimized-writer.enabled写入Parquet文件时是否使用优化的写入器。将此属性设置为false以默认禁用优化的Parquet写入器。相等的目录会话属性是parquet_optimized_writer_enabledtrue
parquet.optimized-writer.validation-percentageparquet.optimized-writer.enabled设置为true时,通过重读整个文件来验证写入后的parquet文件的百分比。相等的目录会话属性是parquet_optimized_writer_validation_percentage。可以通过设置该属性为 0来关闭验证功能。5
parquet.writer.page-sizeParquet写入器的最大页面尺寸。1 MB
parquet.writer.block-sizeParquet写入器的最大行组大小。128 MB
parquet.writer.batch-size在一个批次中,parquet写入器所处理的最大行数。10000
parquet.use-bloom-filter读取Parquet文件时,是否使用bloom过滤器来推倒谓词。将此属性设置为 false,默认情况下禁止使用bloom过滤器。相等的目录会话属性是parquet_use_bloom_filtertrue
parquet.max-read-block-row-count设置一个批次中读取的最大行数。8192
parquet.optimized-nested-reader.enabled当从Parquet文件中读取ARRAY、MAP和ROW类型时,是否应该使用分批列读器以提高性能。将此属性设置为false以默认禁用结构数据类型的优化parquet阅读器。相等的目录会话属性是parquet_optimized_nested_reader_enabledtrue

与Hive 3有关的限制

  • 由于安全原因,sys系统目录无法访问。
  • 不支持Hive的 timestamp with local zone数据类型。可以从具有这种类型的列的表中读出,但不能访问该列数据。不支持向这样的表写入。
  • 由于Hive问题HIVE-21002HIVE-22167,Trino不能正确读取由Hive 3.1或更高版本创建的Parquet、RCBinary或Avro文件格式中的时间戳值。当从这些文件格式读取时,Trino返回的结果与Hive不同。
  • Trino不支持为Hive事务表收集表的统计数据。你必须在创建表后使用Hive的ANALYZE语句来收集表的统计数据。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。