Presto(Trino)动态过滤与优化器

Presto 系列文章目录- 动态过滤与谓词下推


trino性能提升

trino已经快得可笑了。 但是话又说回来,速度越快越好,因此我们进行了以下工作:

  • 改进了将复杂操作下推到连接器的操作,包括聚合下推TopN下推
  • 动态筛选和分区修剪可以极大地提高高选择性联接的性能。
  • WHERE子句中包含IN 的查询的基于成本的决策。
  • Information_schema性能的改进,使需要检查表元数据的第三方BI工具受益,例如DBeaver,Datagrip,Power BI,Tableau,Looker等。
  • 在Parquet和ORC中更快地查询嵌套数据。
  • 基于t-digest数据结构,更快,更准确的roxor_percentile。
  • 在ORC中支持Bloom过滤器。
  • 实验性,优化的Parquet编写器。

新特性

Data sources
Trino empowers you to use one platform to access all data sources. Connectors enable this and we added numerous new connectors:

Iceberg
Prometheus
Oracle
Pinot
Druid
BigQuery
MemSQL

All other connectors received a large host of improvements. Let’s just look at two popular connectors:

Hive connector for HDFS, S3, Azure and cloud object storage systems
Complex Hive views, allows integration with Hive or simplifying migration from Hive
ACID transactional tables with INSERT and DELETE support
Built-in storage caching and support for external caching with Alluxio
New procedures: system.drop_stats(), register_partition(), unregister_partition()
Support for Azure object storage
Support for S3 encrypted files, flexible S3 security mappings and Intelligent-Tiering S3 storage
Elasticsearch connector
The Elasticsearch connector received numerous powerful improvements:

Password authentication
Support for index aliases
Support for array types, Nested, and IP type
Support for Elasticsearch 7.x
Runtime improvements
Operating and maintaining a Trino cluster takes a significant amount of resources. So any work to improve the runtime needs have a significant positive impact:

Requirement to use Java 11, with better GC performance, overall performance, and improved container support
Support for ARM64-based processors to run Trino
Support for minimum number of workers before query starts, useful for implementing autoscaling
Data integrity checks for network transfers to prevent data corruption during processing

Dynamic partition pruning动态分区裁剪

2020年6月14日•Raunaq Morarka,Qubole和Karol Sobczak,星爆数据

星型模式是使用最广泛的数据集市模式之一。星型模式由事实表(通常是分区表)和维度表组成,用于过滤事实表中的行。考虑以下查询,该查询捕获事实表store_sales的常见模式,该表由列ss_sold_date_sk分区,并与过滤后的维度表date_dim结合在一起:

SELECT COUNT(*) FROM 
store_sales JOIN date_dim ON store_sales.ss_sold_date_sk = date_dim.d_date_sk
WHERE d_following_holiday='Y' AND d_year = 2000;

如果没有动态过滤,Presto会将维表的谓词推送到date_dim上的表扫描,但由于查询中的store_sales上没有过滤器,因此它将扫描事实表中的所有数据。由于联接条件具有高度的选择性,联接运算符最终将丢弃大多数探测器侧的行。当前动态过滤的实现对此进行了改进,但是仅限于以ORC或Parquet格式存储的表上的广播联接。此外,它没有利用分区的Hive表的布局。

通过动态分区修剪(扩展了当前的动态过滤实现),每个工作节点都可以从date_dim.d_date_sk列中收集符合联接条件的值,并将其传递给协调器。然后,协调器可以跳过对不符合加入条件的store_sales分区的处理。这大大减少了工作程序节点从store_sales表扫描的数据量。此优化适用于任何存储格式以及广播连接和分区连接。

设计注意事项

这种优化要求工作节点收集的动态过滤器通过网络传递给协调器。我们需要确保此额外的通信开销不会使协调器过载。这是通过将动态过滤器打包到Presto的现有框架中来实现的,以将状态更新从工作人员发送到协调员。

在协调器节点上添加了DynamicFilterService,以异步执行动态过滤器收集。使用此服务注册的查询可以在调度拆分时请求动态过滤器,而不会阻止任何操作。此服务还负责确保连接阶段的所有构建侧任务已完成执行,然后构造动态过滤器以供协调器在探针侧表扫描的调度中使用。

执行

为了确定逻辑计划中动态过滤的机会,我们依赖于#91中添加的实现。动态过滤器被建模为FunctionCall表达式,其计算结果为布尔值。它们在PredicatePushDown优化器规则中从内部联接节点的等联接子句创建,并与其他谓词一起推入计划。在基于成本的优化规则之后,将动态过滤器添加到计划中。这样可以确保动态过滤器不会干扰成本估算和联接重新排序。 PredicatePushDown规则最终可能会通过推理将动态过滤器推送到计划中不受支持的位置。通过添加RemoveUnsupportedDynamicFilters优化器规则来解决此问题,该规则负责确保:

  • 动态过滤器仅在TableScan节点正上方且子树位于某些下游JoinNode的探针侧时才存在
    如果在其探针侧子树上没有使用它的使用者,则将动态过滤器从JoinNode中删除。
  • 我们还在计划阶段的末尾运行DynamicFiltersChecker,以确保优化的计划已满足上述条件。

我们重用LocalExecutionPlanner中现有的DynamicFilterSourceOperator,以从每个工作程序节点上的每个内部联接收集生成端值。除了将收集的TupleDomain传递到同一工作节点中的LocalDynamicFiltersCollector以用于广播连接探针侧扫描外,我们还将它们传递给TaskContext来填充协调器的任务状态更新。

协调器节点上的ContinuousTaskStatusFetcher将所有工作节点上的任务状态更新拉至每个任务。status-refresh-max-wait秒(默认为1秒)或更短(如果任务状态更改)。协调器上的DynamicFilterService定期通过SqlQueryExecution轮询任务状态更新中的动态过滤器,并提供一个接口以在动态过滤器准备就绪时提供它们。 ConnectorSplitManager getSplits API已更新,可以有选择地利用DynamicFilterService提供的动态过滤器。

在Hive连接器中,BackgroundHiveSplitLoader可以通过完全跳过分区中文件的列表来应用动态过滤,或者通过在动态HIFSplitFactory#createInternalHiveSplit中使用动态过滤器(由于延迟的枚举枚举)而避免在拆分的分区中创建拆分来应用动态过滤。

基准测试
我们在5个工作节点上运行了TPC-DS查询

使用以ORC格式存储的数据的r4.8xlarge计算机集群。 TPC-DS表的分区为:

catalog_returns on cr_returned_date_sk
catalog_sales on cs_sold_date_sk
store_returns on sr_returned_date_sk
store_sales on ss_sold_date_sk
web_returns on wr_returned_date_sk
web_sales on ws_sold_date_sk

通过动态分区修剪,以下查询的运行速度提高了20%以上(以秒为单位测量经过时间,以分钟为单位测量CPU时间,以MB为单位测量数据)。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
请注意,此处的基线包括对现有节点本地动态过滤实现的改进。

讨论
为了使动态过滤有效,需要选择较小的尺寸表作为联接的构建侧。基于成本的优化器可以使用metastore中的表统计信息自动执行此操作。因此,我们在运行此基准测试之前已生成表统计信息,并依靠CBO在连接的构建侧正确选择较小的表。

大型事实表按诸如时间之类的维度进行分区是很常见的。将此类表与过滤后的维表结合起来的查询可从动态分区修剪中大大受益。此优化适用于以任何数据格式存储的分区Hive表。它还适用于广播联接和分区联接。通过实现新的ConnectorSplitManager getSplits API,其他连接器可以轻松地利用动态过滤器,该API向连接器提供动态过滤器。

未来的工作

  • 当build-side包含太多值时,支持在DynamicFilterSourceOperator中使用min-max范围。
  • 将动态过滤器从协调器传递回工作节点,以允许ORC和Parquet读取器将动态过滤器与分区联接一起使用。
  • 允许连接器阻止探针侧扫描,直到准备好动态过滤器为止。
  • 支持不等式运算符的动态过滤
  • 支持semi-joins
  • 充分利用除Hive之外的连接器中的动态过滤器。

Hive connector

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

对于以ORC或Parquet文件格式存储的表,动态过滤器也被推送到工作节点上的本地表扫描中以进行广播联接。推送到ORC和Parquet读取器中的动态过滤器谓词用于执行 stripe or rowgroup修剪并保存在磁盘I / O上。通过联接条件中使用的列对ORC或Parquet文件中的数据进行排序,可以显着提高stripe or rowgroup修剪的效率。这是因为将相似的数据分组到同一stripe or rowgroup中可以大大提高在stripe or rowgroup级别上维护的最小/最大索引的选择性。

延迟执行动态过滤器

在开始表扫描之前,等待动态过滤器的收集通常是有益的。如果动态筛选能够减少扫描的数据量,那么额外的等待时间可能潜在地大大节省查询和CPU时间。

对于Hive连接器,可以使用catalog file或 catalog session property < hive-catalog> hive.dynamic-filtering-probe-blocking-timeout将表扫描延迟配置的时间,直到收集动态过滤器为止。 < hive-catalog>.dynamic_filtering_probe_blocking_timeout。

动态过滤

动态筛选优化,通过避免读取将被联接条件过滤的数据,从而显着提高了具有选择性联接的查询的性能。

考虑以下查询,该查询捕获事实表store_sales与过滤后的维度表date_dim结合的常见模式:

SELECT count(*) FROM store_sales JOIN date_dim ON store_sales.ss_sold_date_sk = date_dim.d_date_sk WHERE d_following_holiday='Y' AND d_year = 2000;

如果没有动态过滤,Trino会将维度表的谓词推送到date_dim上的表扫描,并且由于查询中的store_sales上没有过滤器,因此它会扫描事实表中的所有数据。由于联接条件具有高度的选择性,联接运算符最终会丢弃大多数探针侧的行。

启用动态过滤后,Trino将从联接右侧的已处理维表中收集联接条件的候选值。对于广播联接,将从此集合生成的运行时谓词推入在同一工作程序上运行的联接左侧的本地表扫描中

另外,这些运行时谓词通过网络与协调器通信,以便在枚举表扫描分片期间也可以在协调器上执行动态过滤。

例如,在Hive连接器的情况下,动态过滤器用于跳过不符合联接条件的分区的加载。这称为动态分区修剪

动态过滤优化的结果可以包括以下好处:

  • 改善了整体查询性能
  • 减少Trino和数据源之间的网络流量
  • 减少远程数据源上的负载

对动态过滤器下推的支持决定于每个连接器以及相关的基础数据库或存储系统。

分析和确认

动态过滤取决于许多因素:
计划程序支持Trino中给定联接操作的动态过滤。

  • 当前使用=,<,<=,>,> =IS NOT DISTINCT FROM联接条件进行的内部联接和右联接,并且支持具有IN条件的semi-joins

  • 连接器支持在运行时利用推入表的动态过滤器进行扫描。例如,Hive连接器可以将动态过滤器推入ORC和Parquet读取器中,以执行 stripe or rowgroup修剪。

  • 支持在拆分枚举阶段使用动态过滤器的连接器。

  • 联接的右侧(生成)侧的大小。

您可以仔细查看查询的EXPLAIN计划,以分析计划者是否正在向特定查询的计划中添加动态过滤器。例如,可以通过运行以下语句来获取上述查询的解释计划:

EXPLAIN
SELECT count(*)
FROM store_sales
JOIN date_dim ON store_sales.ss_sold_date_sk = date_dim.d_date_sk
WHERE d_following_holiday='Y' AND d_year = 2000;

该查询的解释计划在InnerJoin节点中显示dynamicFilterAssignments,并从构建符号d_date_sk中收集了动态过滤器df_370。您还可以在Hive ScanFilterProject运算符中看到dynamicFilter谓词,其中df_370与探测符号ss_sold_date_sk关联。这表明计划程序成功将动态过滤器下推到查询计划中的连接器。

...

Fragment 1 [SOURCE]
    Output layout: [count_3]
    Output partitioning: SINGLE []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    Aggregate(PARTIAL)
    │   Layout: [count_3:bigint]
    │   count_3 := count(*)
    └─ InnerJoin[(""ss_sold_date_sk"" = ""d_date_sk"")][$hashvalue, $hashvalue_4]
       │   Layout: []
       │   Estimates: {rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}
       │   Distribution: REPLICATED
       │   dynamicFilterAssignments = {d_date_sk -> df_370}
       ├─ ScanFilterProject[table = hive:default:store_sales, grouped = false, filterPredicate = true, dynamicFilter = {""ss_sold_date_sk"" = #df_370}]
       │      Layout: [ss_sold_date_sk:bigint, $hashvalue:bigint]
       │      Estimates: {rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}/{rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}/{rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}
       │      $hashvalue := combine_hash(bigint '0', COALESCE(""$operator$hash_code""(""ss_sold_date_sk""), 0))
       │      ss_sold_date_sk := ss_sold_date_sk:bigint:REGULAR
       └─ LocalExchange[HASH][$hashvalue_4] (""d_date_sk"")
          │   Layout: [d_date_sk:bigint, $hashvalue_4:bigint]
          │   Estimates: {rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}
          └─ RemoteSource[2]
                 Layout: [d_date_sk:bigint, $hashvalue_5:bigint]

Fragment 2 [SOURCE]
    Output layout: [d_date_sk, $hashvalue_6]
    Output partitioning: BROADCAST []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    ScanFilterProject[table = hive:default:date_dim, grouped = false, filterPredicate = ((""d_following_holiday"" = CAST('Y' AS char(1))) AND (""d_year"" = 2000))]
        Layout: [d_date_sk:bigint, $hashvalue_6:bigint]
        Estimates: {rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}/{rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}/{rows: 0 (0B), cpu: 0, memory: 0B, network: 0B}
        $hashvalue_6 := combine_hash(bigint '0', COALESCE(""$operator$hash_code""(""d_date_sk""), 0))
        d_following_holiday := d_following_holiday:char(1):REGULAR
        d_date_sk := d_date_sk:bigint:REGULAR
        d_year := d_year:int:REGULAR

在使用动态过滤器执行查询的过程中,Trino在可通过Web UI使用的QueryInfo JSON中填充有关动态过滤器的统计信息。 在queryStats部分中,可以在dynamicFiltersStats结构中找到有关由协调节点收集的动态过滤器的统计信息

"dynamicFiltersStats" : {
      "dynamicFilterDomainStats" : [ {
        "dynamicFilterId" : "df_370",
        "simplifiedDomain" : "[ [[2451546, 2451905]] ]",
        "rangeCount" : 3,
        "discreteValuesCount" : 0,
        "collectionDuration" : "2.34s"
      } ],
      "lazyDynamicFilters" : 1,
      "replicatedDynamicFilters" : 1,
      "totalDynamicFilters" : 1,
      "dynamicFiltersCompleted" : 1
}

可以通过查看该表扫描的操作员统计信息来验证将动态过滤器下推到工作节点上的表扫描中的情况。 dynamicFilterSplitsProcessed记录将动态过滤器下推到表扫描后处理的分片数。

"operatorType" : "ScanFilterAndProjectOperator",
"totalDrivers" : 1,
"addInputCalls" : 762,
"addInputWall" : "0.00ns",
"addInputCpu" : "0.00ns",
"physicalInputDataSize" : "0B",
"physicalInputPositions" : 28800991,
"inputPositions" : 28800991,
"dynamicFilterSplitsProcessed" : 1,

动态过滤器收集阈值

为了使动态过滤有效,需要选择较小的尺寸表作为联接的构建侧。基于成本的优化器可以使用连接器提供的表统计信息自动执行此操作。因此,建议使表统计信息保持最新,并依靠CBO在连接的构建侧正确选择较小的表

从build构建端收集用于动态过滤的联接键列的值可能会在查询执行期间招致额外的CPU开销。因此,为了将收集动态过滤器的开销限制在可能有选择性的联接运算符的情况下,Trino定义了从构建侧任务收集的动态过滤器大小的阈值。可以使用enable-large-dynamic-filters配置属性或enable_large_dynamic_filters会话属性来启用用于具有较大构建侧的联接的动态过滤器的集合。

启用大型动态过滤器后,可以使用配置属性dynamic-filtering.large-broadcast.max-distinct-values-per-driver,dynamic-filtering.large-为每种联接分配类型配置动态过滤器的大小限制。及 broadcast.max-per-per-driverdynamic-filtering.large-broadcast.range-row-limit-per-driver为其分区联接分配类型的等效项。

同样,可以使用配置属性(例如,每个驱动程序dynamic-filtering.large-partitioned.max-distinct-values-per-driver,dynamic-filtering.large-partitioned)配置未启用enable-large-dynamic-filters时的动态过滤器限制。每个驱动程序的max-size-per-driverdynamic-filtering.large-partitioned.range-row-limit-per-driver及其与广播连接分发类型等效的等效项。

基于每个驱动程序max-distinct-valuesmax-size-perdriver的属性定义了阈值,该阈值取决于在不同值数据结构中收集动态过滤器的大小。当构建侧超过这些阈值时,Trino切换到收集每列的最小值和最大值以减少开销。此最小-最大过滤器的粒度远小于不同值过滤器的粒度。但是,从探针侧过滤某些数据可能仍然是有益的,尤其是当从连接的构建侧选择了一系列值时。最小-最大过滤器收集的限制由基于每个驱动器的范围行限制的属性定义。

维度表布局

对于表键与列相关联的维度表,动态过滤最有效。

例如,日期维键 列应与日期列相关联,以便表键随日期值单调增加。地址维键可以由其他列组成,例如COUNTRY-STATE-ZIP-ADDRESS_ID,示例值为US-NY-10001-1234。这种用法即使在维表中有大量选定的行也可以成功进行动态过滤。

局限性

  • 当前仅对Hive连接器和Memory连接器实施动态过滤。

  • 将动态过滤器下推到工作节点上的本地表扫描仅限于广播联接

  • DOUBLE,REAL和未排序序数据类型不支持最小-最大动态过滤器集合

  • 使用IS NOT DISTINCT FROM谓词时,DOUBLEREAL数据类型不支持动态过滤。

  • 当连接键包含从构建键类型到探测键类型的转换时,支持动态过滤。

  • 当从探测键类型到构建键类型隐式转换时,在有限的方案中还支持动态过滤。例如,当构建侧键为DOUBLE类型,而探针侧键为REAL或INTEGER类型时,则支持动态过滤。


以下为Optimizer内容


PUSHDOWN 下推

Trino可以将查询或部分查询的处理下推到连接的数据源中。这意味着将特定谓词,聚合函数或其他操作传递到基础数据库或存储系统进行处理。

此下推的结果可以包括以下好处:

  • 改善了整体查询性能

  • 减少Trino和数据源之间的网络流量

  • 减少远程数据源上的负载

对下推的支持特定于每个连接器以及相关的基础数据库或存储系统

聚合算子下推

如果满足以下条件,则可以进行聚合下推:

  • 如果连接器通常支持聚合下推。

  • 如果连接器支持特定功能的下推。

  • 查询结构是否允许下推发生。

您可以通过查看查询的EXPLAIN计划来检查是否对特定查询进行了下推。如果将聚合功能成功下推到连接器,则说明计划不会显示该聚合运算符。解释计划仅显示Trino执行的操作

例如,我们将TPCH数据集加载到PostgreSQL数据库中,然后使用PostgreSQL连接器查询它:

SELECT regionkey, count(*)
FROM nation
GROUP BY regionkey;

You can get the explain plan by prepending the above query with EXPLAIN:

EXPLAIN
SELECT regionkey, count(*)
FROM nation
GROUP BY regionkey;

该查询的解释计划没有显示任何带有count函数的Aggregate运算符,因为此操作现在由连接器执行。 您可以在PostgreSQL TableScan运算子中看到count(*)函数。 这表明您下推成功。

Fragment 0 [SINGLE]
    Output layout: [regionkey_0, _generated_1]
    Output partitioning: SINGLE []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    Output[regionkey, _col1]
    │   Layout: [regionkey_0:bigint, _generated_1:bigint]
    │   Estimates: {rows: ? (?), cpu: ?, memory: 0B, network: ?}
    │   regionkey := regionkey_0
    │   _col1 := _generated_1
    └─ RemoteSource[1]
            Layout: [regionkey_0:bigint, _generated_1:bigint]

Fragment 1 [SOURCE]
    Output layout: [regionkey_0, _generated_1]
    Output partitioning: SINGLE []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    TableScan[postgresql:tpch.nation tpch.nation columns=[regionkey:bigint:int8, count(*):_generated_1:bigint:bigint] groupingSets=[[regionkey:bigint:int8]], gro
        Layout: [regionkey_0:bigint, _generated_1:bigint]
        Estimates: {rows: ? (?), cpu: ?, memory: 0B, network: 0B}
        _generated_1 := count(*):_generated_1:bigint:bigint
        regionkey_0 := regionkey:bigint:int8

您可以通过在上面的查询中加上EXPLAIN来获得解释计划:
许多因素可以阻止下推

  • 向查询添加条件

  • 使用无法向下推入连接器的其他聚合函数

  • 使用不具有特定功能下推支持的连接器

结果,该查询的解释说明计划显示了Trino正在执行的汇总操作。 这是一个明显的信号,表明现在不执行下推到远程数据源的操作,而是由Trino执行聚合处理。

Fragment 0 [SINGLE]
    Output layout: [regionkey, count]
    Output partitioning: SINGLE []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    Output[regionkey, _col1]
    │   Layout: [regionkey:bigint, count:bigint]
    │   Estimates: {rows: ? (?), cpu: ?, memory: ?, network: ?}
    │   _col1 := count
    └─ RemoteSource[1]
           Layout: [regionkey:bigint, count:bigint]

Fragment 1 [HASH]
    Output layout: [regionkey, count]
    Output partitioning: SINGLE []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    Aggregate(FINAL)[regionkey]
    │   Layout: [regionkey:bigint, count:bigint]
    │   Estimates: {rows: ? (?), cpu: ?, memory: ?, network: ?}
    │   count := count("count_0")
    └─ LocalExchange[HASH][$hashvalue] ("regionkey")
       │   Layout: [regionkey:bigint, count_0:bigint, $hashvalue:bigint]
       │   Estimates: {rows: ? (?), cpu: ?, memory: ?, network: ?}
       └─ RemoteSource[2]
              Layout: [regionkey:bigint, count_0:bigint, $hashvalue_1:bigint]

Fragment 2 [SOURCE]
    Output layout: [regionkey, count_0, $hashvalue_2]
    Output partitioning: HASH [regionkey][$hashvalue_2]
    Stage Execution Strategy: UNGROUPED_EXECUTION
    Project[]
    │   Layout: [regionkey:bigint, count_0:bigint, $hashvalue_2:bigint]
    │   Estimates: {rows: ? (?), cpu: ?, memory: ?, network: ?}
    │   $hashvalue_2 := combine_hash(bigint '0', COALESCE("$operator$hash_code"("regionkey"), 0))
    └─ Aggregate(PARTIAL)[regionkey]
       │   Layout: [regionkey:bigint, count_0:bigint]
       │   count_0 := count(*)
       └─ TableScan[tpch:nation:sf0.01, grouped = false]
              Layout: [regionkey:bigint]
              Estimates: {rows: 25 (225B), cpu: 225, memory: 0B, network: 0B}
              regionkey := tpch:regionkey

计划没有显示任何带有count函数的Aggregate运算符,因为此操作现在由连接器执行。您可以在PostgreSQL TableScan运算子中看到count(*)函数。这表明您下推成功。

局限性

下推式广告不支持许多更复杂的语句:

  • 复杂的分组操作,例如ROLLUP,CUBEGROUPING SETS

  • 聚合函数内部的表达式调用:sum(a * b)

  • 强制:sum(integer_column)

  • 有序聚合
    aggregations with ordering

  • 过滤器聚合
    aggregations with filter

Cost based optimizations 基于成本的优化——CBO优化器

Trino支持以下几种基于成本的优化。

Join enumeration

在查询中执行联接的顺序可能会对查询的性能产生重大影响。对性能影响最大的连接顺序是在网络上处理和传输的数据的大小。如果在执行的早期执行了会产生大量数据的联接,则后续阶段需要处理大量数据的时间超过了必要的时间,从而增加了查询所需的时间和资源。

通过基于成本的联接枚举,Trino使用连接器提供的表统计信息来估算不同联接订单的成本,并自动选择计算成本最低的联接订单。

联接枚举策略由join_reordering_strategy会话属性控制,optimizer.join-reordering-strategy配置属性提供默认值。

有效值为:

  • AUTOMATIC(默认)-启用全自动联接枚举

  • ELIMINATE_CROSS_JOINS-消除不必要的交叉联接

  • NONE-纯粹的语法连接顺序

如果使用AUTOMATIC且统计信息不可用,或者由于任何其他原因无法计算成本,则使用ELIMINATE_CROSS_JOINS策略。

Join distribution selection

Trino使用基于哈希的联接算法。这意味着对于每个联接运算符,必​​须从一个联接输入(称为构建端)创建哈希表。然后,对另一个输入(称为探测端)进行迭代,并查询哈希表的每一行以找到匹配的行。

有两种类型的联接分布:

  • Partitioned:参与查询的每个节点仅根据部分数据构建哈希表

  • Broadcast:参与查询的每个节点都根据所有数据构建一个哈希表(数据复制到每个节点

每种类型都有其取舍。分区join要求使用联接键的哈希值重新分配两个表。与广播jion相比,这可能会慢一些,有时甚至会慢很多,但允许更大的jion。特别是,如果构建侧比探测侧小得多,则广播join会更快。但是,广播联接要求过滤后的联接构建侧的表适合每个节点上的内存,而分布式联接只需要适合所有节点上的分布式内存。

通过基于成本的联接分配选择,Trino会自动选择使用分区联接或广播联接。通过基于成本的连接枚举,Trino会自动选择探针侧和构建侧。

联接分配策略由join_distribution_type会话属性控制,join-distribution-type配置属性提供默认值。

有效值为:

  • 自动(默认)-为每个联接自动确定联接分布类型

  • 广播-广播联接分发用于所有联接

  • 分区-分区联接分配用于所有联接

限制复制表的大小

将联接重新排序策略设置为AUTOMATIC或将联接分配类型设置为AUTOMATIC时,将自动选择联接分配类型。在这种情况下,可以通过join-max-broadcast-table-size配置属性(例如,join-max-broadcast-table-size = 100MB)或通过join_max_broadcast_table_size会话属性(例如,设置会话join_max_broadcast_table_size='100MB'来限制复制表的最大大小。;)这样可以提高群集的并发性,并防止在CBO错误估计联接表的大小时出现错误的计划

默认情况下,复制表的大小上限为100MB

连接器实现

为了使Trino优化器使用基于成本的策略,连接器实现必须提供表统计信息。
Trino支持基于统计的查询优化。为了使查询能够利用这些优化,Trino必须具有该查询中表的统计信息。

表统计信息

通过连接器提供给查询计划者

可用统计信息

Trino提供以下统计信息:
对于表:

  • 行数:表中的总行数

对于表中的每一列:

  • 数据大小:需要读取的数据大小
  • 空值分数:空值的分数
  • 不重复值计数:不重复值的数量
  • 低值:列中的最小值
  • 高值:列中的最大值

可用于特定查询的统计信息集取决于所使用的连接器,并且还可能因表而异。例如,Hive连接器当前不提供有关数据大小的统计信息。

可以使用SHOW STATS命令通过Trino SQL界面显示表统计信息。对于Hive连接器,请参考Hive连接器文档以了解如何更新表统计信息。
Hive连接器支持收集和管理表统计信息,以提高查询处理性能。

写入数据时,Hive连接器始终收集基本统计信息(numFiles,numRows,rawDataSize,totalSize),并且默认情况下还将收集列级统计信息:
在这里插入图片描述

COST IN EXPLAIN

在计划期间,将基于查询中表的表统计信息来计算与计划的每个节点关联的成本。计算出的成本将作为EXPLAIN语句输出的一部分进行打印。

成本信息以{rows: XX (XX), cpu: XX, memory: XX, network: XX}. rows格式显示在计划树中。行是指每个计划节点在执行过程中输出的预期行数。行数后括号中的值是指每个计划节点输出的数据的预期大小(以字节为单位)。其他参数指示计划节点的执行所使用的CPU,内存和网络的估计数量。这些值不代表任何实际单位,而是用于比较计划节点之间的相对成本的数字,从而使优化器可以选择最佳计划来执行查询。如果不知道任何值,请输入打印。

例如:

EXPLAIN SELECT comment FROM tpch.sf1.nation WHERE nationkey > 3;
- Output[comment] => [[comment]]
        Estimates: {rows: 22 (1.69kB), cpu: 6148.25, memory: 0.00, network: 1734.25}
    - RemoteExchange[GATHER] => [[comment]]
            Estimates: {rows: 22 (1.69kB), cpu: 6148.25, memory: 0.00, network: 1734.25}
        - ScanFilterProject[table = tpch:nation:sf1.0, filterPredicate = ("nationkey" > BIGINT '3')] => [[comment]]
                Estimates: {rows: 25 (1.94kB), cpu: 2207.00, memory: 0.00, network: 0.00}/{rows: 22 (1.69kB), cpu: 4414.00, memory: 0.00, network: 0.00}/{rows: 22 (1.69kB), cpu: 6148.25, memory: 0.00, network: 0.00}
                nationkey := tpch:nationkey
                comment := tpch:comment

除了实际的运行时统计信息外,估计成本还会在EXPLAIN ANALYZE中打印

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值