Trino简介

Trono文档地址https://trino.io/docs/current/

Trino

一、简介

  1. Trino是通过分布式查询,高效处理大量数据的工具。要处理TB或PB级别的数据,一般是使用能够与Hadoop和HDFS进行交互的工具。Trino的设计目标就是取代这些工具,如Hive或Pig,

  2. 但是除了访问HDFS。Trino也可以操作其他数据源,包括传统关系数据库和其他数据源,如Cassandra。

  3. Trino旨在处理数据仓库和分析工作:数据分析、聚合大量数据并生成报告。这些工作负载通常被归类为在线分析处理(OLAP)。

二、基础概念

2.1 架构

  • Trino是一个分布式查询引擎,在多个服务器上并行处理数据。
  • Trino由两种server类型:CoordinatorWorker

Cluster

  • Trino集群由一个Coordinator和多个Worker组成
  • 用户发送SQL到CoordinatorcoordinatorWorker协同工作。访问已连接的数据源,数据源在catalogs中配置
  • Coordinator负责解析SQL,分配工作到Worker,然后并行查询

Coordinator

  • 每个集群都必须有一个Coordinator,可以由Worker兼任
  • 负责解析语句、制定查询计划和管理Worker
  • 跟踪每个Worker,协调查询的执行。
  • 创建查询的逻辑模型,分解为一系列Stage,然后将其转换为在一组Worker上运行的一系列连接Task
  • 使用REST API与Workerclient进行通信。

Worker

  • 负责执行Task和处理数据
  • 从连接器中获取数据,并与其他Worker交换中间数据。CoordinatorWorker获取结果,并将结果返回给客户端。
  • 使用REST API与其他WorkerCoordinator进行通信。
  • 启动时,会主动向Coordinator注册自己。

2.2 Data sources

Connector

  • 用于连接数据源,支持各种数据源:传统数据库,NoSQL,Hive等。

  • 每个Catalog都与一个Connector关联。查看Catalog配置文件,会发现每个文件都包含一个必填属性connector.name,用于指定Connector

  • 可以同时连接多个数据源。例如,有两个Hive集群,就可以配置两个catalog,都使用Hive Connector,这样就可以在一个SQL查询中同时查询两个Hive集群的数据。

Catalog(目录)

  • 一个Catalog,包含Schemas和通过Connector连接的数据源

  • 当引用表时,表名始终以Catalog为根。例如,hive.test_data.test的指的是hive Catalogtest_data schema中的test table

  • Catalog存储在Trino配置目录中,通过properties文件定义。

Schema(模式)

  • Schema是用于组织表的一种概念。定义了表的命名空间,帮助对表进行分类和组织。一个Schema可以包含多个表,并在逻辑上将这些表进行分组。
  • 不同的Connector可以以不同的方式组织表和模式。
    • 对于关系型数据库连接器,Schema通常对应于数据库中的Schema或命名空间,帮助将表进行逻辑上的划分和组织。
    • 对于Hive连接器,Schema可以对应于Hive数据库中的一个数据库或一个文件夹,用于将表组织在一起。
  • 通过使用Schema,可以更好地组织和管理Trino中的表,提供更清晰的命名空间和上下文。有助于在查询中指定表的完全限定名称,并提供更好的可读性和维护性。

Table

  • 表示实际的数据表或数据集,是存储和组织实际数据的实体。
  • 每个表都属于一个Schema,每个Schema又属于一个Catalog。
  • 表定义了数据的结构(列名、数据类型等)以及存储在其中的实际数据。
  • 通过在Trino中查询表,可以对数据进行检索、过滤、转换和分析等操作。

2.3查询执行模型

Trino将SQL语句转换为查询,然后在集群上执行

Statement(语句)

Query(查询)

  • Query是用户提交给Trino执行的查询。
  • 包含了从数据源中检索、转换、过滤和分析数据的指令。
  • Trino接收查询并将其分解为一系列的阶段(Stage)来执行。

Stage

  • Stage是查询执行计划中的一个单元,表示查询的一个并行处理阶段。

  • 每个Stage包含一个或多个任务(Tasks),用于并行处理数据。Stage可以并行的,也可以串行,取决于查询执行计划。

  • 组成查询的Stage层次结构类似于一棵树。每个查询都有一个根Stage,负责聚合其他Stage的输出。

  • Stage是一个逻辑概念,包含了多个任务(Tasks),因此Stage本身是不运行的。

Task

  • Task是在Trino中执行的基本工作单元。
  • 每个Task负责处理一部分的数据或计算,并生成部分结果。
  • Task在不同的工作节点上并行执行,以加速查询处理。
  • 一个Stage可以由多个Task组成,每个Task处理不同的数据分片(Splits)。

Split

  • Split是数据的逻辑切片或分块,可以根据数据的位置、大小或其他标准进行划分。
  • 每个Split代表了查询要处理的数据的一个子集。
  • Task通过处理不同的Split来并行执行查询操作。

Driver

当用户提交一个查询给Trino时,Driver首先接收到这个查询,并负责解析查询语句、生成查询执行计划和执行该计划。Driver会将查询拆分成一系列的阶段(Stages)和任务(Tasks),并将任务分发到不同的工作节点上并行执行。

  • Driver是Trino执行计划的主要组件之一
  • 一个Task包含一个或多个并行的Driver
  • Driver对数据进行操作,组合Operator以生成输出,然后由Task进行聚合,之后交付给另一个Stage的另一个Task
  • Driver是一系列操作实例,是Trino中最低级别的可并行组件。Driver有一个输入和一个输出。
  • Driver负责监控和调度各个任务的执行,收集和合并任务的结果,并最终将最终结果返回给客户端。它还负责处理查询中的错误和异常情况,并提供查询的元数据信息。

Operator

  • Trino查询计划中的执行单元,用于实现不同的操作和转换。
  • 扫描表、过滤数据、连接数据等都是通过Operator来完成的。
  • 操作符接收输入数据,经过计算和转换后产生输出数据。

Exchange

Exchange用于在不同任务之间进行数据传输和交换。当需要与其他任务交换数据时,就插入Exchange到执行计划中,用于发送和接收数据。

三、查询优化

3.1 基于查询成本的优化

Join策略枚举

  • 在查询中,Join的执行顺序对性能有着重要的影响,修改Join顺序,优化查询,就能减少所需的时间和资源。

  • Join中对性能影响最大的因素是处理和传输数据的大小,如果在早期执行了会产生大量数据的Join,那么随后的阶段就需要处理大量数据,增加了所需的时间和资源。

  • Trino使用连接器提供的表统计信息来估算不同Join顺序的成本,并自动选择最低成本的Join顺序。

  • Join枚举策略由join_reordering_strategy属性控制

    • 取值有:
      • AUTOMATIC(默认值): 启用完全自动的Join枚举
      • ELIMINATE_CROSS_JOINS: 消除不必要的交叉Join
      • NONE: 查询语句指定的Join顺序
  • 默认使用AUTOMATIC策略,但是如果不能从连接器获得任何可用的统计信息,或者由于一些原因,无法计算成本,则会切换到ELIMINATE_CROSS_JOINS策略。

Join distribution selection

  • Trino使用基于哈希的Join算法。

  • 对于每个Join操作,对连接输入方(称为构建方)构建一个哈希表。然后对另一个输入方(称为探测方)的数据进行枚举。对于探测方的每一行数据,都会查询构建方的哈希表以找到匹配的行。

  • join distribution有两种类型:

    • 分区Partitioned:参与查询的每个节点只对数据的一部分构建哈希表。

    • 广播Broadcast:参与查询的每个节点都对所有数据构建哈希表。数据会被复制到每个节点上。

  • 两种类型各有优缺点。

    • 分区Join需要使用Join的Key的哈希值分配两个表的数据到不同分区的节点。因此分区Join可能比广播Join慢很多,但它们允许更大数据量的Join。
    • 如果构建方远小于探测方,广播Join速度更快。
    • 广播Join会将构建方的表复制到每个节点,需要每个节点都有足够的内存来容纳数据。
    • 分区Join,表的数据分布在多个节点上,每个节点只存储部分数据。因此,只需构建侧的表小于整个集群内存的总和即可。
  • 通过基于成本的 join distribution selection,Trino自动选择使用分区广播Join。通过基于成本的Join策略枚举,Trino自动选择构建方和探测方。

  • Join distribution策略由join_distribution_type属性控制

    • 可选值包括:
      • AUTOMATIC(默认值):策略由系统自动确定
      • BROADCAST: 所有连接使用广播连接
      • PARTITIONED:所有连接使用分区连接

限制复制表大小

"复制表"指的是在广播连接(Broadcast Join)中将数据复制到每个参与连接的节点上的表。

广播连接中,通常将小表的数据复制到所有节点上,以确保每个节点都具有完整的数据副本。这样可以避免在连接过程中进行网络通信和数据传输,提高查询的执行速度。因此,复制表是指被复制到每个节点上的表,使得每个节点都能够独立地执行连接操作,而无需通过网络传输数据。

join reordering strategyjoin distribution type设置为AUTOMATIC时,会自动选择join distribution type。在这两种情况下,可以通过join-max-broadcast-table-sizejoin_max_broadcast_table_size属性来限制复制表的最大值。提高集群并发性,并防止成本优化器错误估计连接表大小时出现错误计划。

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

Syntactic join order

如果不使用优化,Trino默认将使用语法的Join顺序。虽然没有正式的方法来优化此类查询,但可以利用Trino实现Join的方式来提高性能。

Trino使用基于内存的哈希Join。在处理Join语句时,Trino将Join最右侧的表加载到内存中作为构建侧,然后将第二右的表作为探测方进行Join操作。

若有多个Join,则第一个Join的结果将保留在内存中作为构建侧,然后第三右的表作为探测方,之后以此类推。当连接顺序变得更复杂,例如使用括号指定连接的父级时,Trino可能会同时执行多个较低级别的Join,但每个步骤的逻辑都是相同的,最终将结果Join在一起时也是如此。

基于上述分析,SQL最佳的写法是,大表到小表从左到右依次Join,即大表放左边,小表放右边,以减少内存使用量。

SELECT
  *
FROM
  large_table l
  LEFT JOIN medium_table m ON l.user_id = m.user_id
  LEFT JOIN small_table s ON s.user_id = l.user_id

这种优化方式并不是Trino提供的特性,而是基于Join操作的底层实现方式,所产生的优化方法,因此这种行为在之后的版本可能会发生变化。

连接器实现

为了让Trino优化器使用基于成本的策略,连接器必须能提供表统计信息。

3.2 Pushdown

"Pushdown"是一种优化技术,指的是将数据处理操作(如过滤、聚合、排序等)推送到数据存储或计算引擎中进行处理,而不是在应用程序或上层框架中进行。

比如在从数据库取数据的阶段就执行部分where条件进行过滤,减少数据量。

条件下推

Predicate pushdown

将过滤操作推送到数据存储引擎中,在数据检索阶段进行过滤,减少需要传输和处理的数据量。例如,在关系型数据库中,将WHERE条件下推到数据库引擎中进行过滤。

投影下推

Projection Pushdown

投影操作用于选择查询结果中所需的列(字段),过滤掉不需要的列,以减少传输和处理的数据量。而投影下推技术将这个投影操作下推到数据源层面,使数据源能够在查询执行过程中只返回所需的列,而不返回所有列,减少需要传输和处理的列数。减少网络开销和计算资源的消耗。

如果投影下推成功,查询的EXPLAIN计划仅访问表扫描操作中相关的列。

解引用下推

Dereference pushdown

用于减少数据的传输和处理开销。

在数据查询过程中,有时需要对复杂的数据结构进行解引用,即从一个复杂对象中提取特定的字段或属性。这种解引用操作可能涉及多个数据项和嵌套的结构,如果在上层应用程序或查询处理层中进行解引用,可能需要将整个数据结构加载到内存中,再进行解析和处理,增加了计算和传输的开销。

例如,考虑在Hive连接器中有一个包含多个字段的ROW类型列的表格。如果查询只访问一个字段,解引用下推允许文件读取器仅读取该行中的单个字段。对于嵌套在顶层行中的行的字段,也是同样的情况。这可以在从存储系统读取的数据量上实现显著的节省。

聚合下推

Aggregation Pushdown

将聚合操作(如SUM、COUNT、AVG等)推送到数据存储引擎中进行计算,减少数据传输和处理的工作量。

只有在满足以下条件的情况下,聚合下推才能进行:

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

  • 如果连接器支持特定函数或多个函数的下推。

  • 如果查询结构允许进行下推。

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

Join下推

Join pushdown

连接下推允许连接器将Join操作委托给底层数据源。这可能会带来性能提升,并使Trino可以在更少的数据上进行处理。

对于支持连接下推的具体规则,每个数据源和连接器可能会有所不同。

然而,有一些通用条件必须满足才能进行连接下推:

  • 所有作为Join的谓词必须可以下推执行

  • 连接的表必须来自同一个catalog

可以通过查看Explain来验证是否进行了下推。如果Join被连接器下推到数据源,则Explain不会显示Join运算符:

Limit pushdown

在查询过程中,Limit用于限制查询结果集的大小,通常是指返回前N条记录或跳过前N条记录。而限制下推技术将Limit下推到数据源层面,使数据源能够在查询执行过程中仅返回满足限制条件的记录,而不返回整个结果集。

Top-N pushdown

LIMIT或FETCH FIRST与ORDER BY组合,在大型有序数据集中创建了一个要返回的小集合。依赖于顺序来确定需要返回哪些记录。

这种查询的pushdown被称为Top-N pushdown,该操作返回前N行。使连接器能够将此类查询的处理推送到底层数据源,从而显著减少传输到Trino并处理的数据量。

比如

SELECT id, name
FROM postgresql.public.company
ORDER BY id
LIMIT 5;

如果Connector支持,那就会在查询阶段进行下推

// TableScan进行了下推
Fragment 1 [SOURCE]
    Output layout: [id, name]
    Output partitioning: SINGLE []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    TableScan[postgresql:public.company public.company sortOrder=[id:integer:int4 ASC NULLS LAST] limit=5, grouped = false]
        Layout: [id:integer, name:varchar]
        Estimates: {rows: ? (?), cpu: ?, memory: 0B, network: 0B}
        name := name:varchar:text
        id := id:integer:int4

如果不支持,就必须由Trino进行Top-N查询

// TopNPartial,没有下推
Fragment 1 [SOURCE]
    Output layout: [custkey, name]
    Output partitioning: SINGLE []
    Stage Execution Strategy: UNGROUPED_EXECUTION
    TopNPartial[5 by (custkey ASC NULLS LAST)]
    │   Layout: [custkey:bigint, name:varchar(25)]
    └─ TableScan[tpch:customer:sf1.0, grouped = false]
           Layout: [custkey:bigint, name:varchar(25)]
           Estimates: {rows: 150000 (4.58MB), cpu: 4.58M, memory: 0B, network: 0B}
           custkey := tpch:custkey
           name := tpch:name
posted @ 2023-07-08 15:27  INnoVation-V2  阅读(549)  评论(0编辑  收藏  举报