《Presto系列文章-CHAPTER 4 Presto Architecture》

Presto(暂译:普雷斯托)系列文章目录-CHAPTER 4 Presto Architecture

在这里插入图片描述


前言

Presto: The Definitive Guide SQL at Any Scale, on Any Storage,in Any Environment
——Matt Fuller, Manfred Moser, and Martin Traverso

本书共311页大致目录:[免费电子版下载](https://gitee.com/fanqingle/books/tree/master/)
  • Part I. Getting Started with Presto
    1. Introducing Presto.
    1. Installing and Con€guring Presto
    1. Using Presto
  • Part II. Diving Deeper into Presto
    1. **Presto Architecture**
    1. Production-Ready Deployment
    1. Connectors
    1. Advanced Connector Examples
    1. Using SQL in Presto.
    1. Advanced SQL
  • Part III. Presto in Real-World Uses
    1. Security
    1. Integrating Presto with Other Tools
    1. Presto in Production.
    1. Real-World Examples
    1. Conclusion.
  • Index

提示:以下是本篇文章正文内容,非专业翻译,下面翻译仅供参考

CHAPTER 4 Chapter 4: Presto Architecture

介绍Presto之后,以及更早版本中的初始安装和使用在各章中,我们现在讨论Presto架构。我们将深入探讨相关概念,因此您可以了解Presto查询执行模型,查询计划和基于成本的优化。

在本章中,我们首先讨论Presto高级架构组件。它是全面了解Presto的工作方式非常重要,特别是如果您打算自己安装和管理Presto群集,如第5章所述。在本章的后半部分,当我们讨论时,我们会更深入地研究这些组件关于Presto的查询执行模型。这是最重要的,如果您需要诊断或调整性能低下的查询,所有这些都将在第8章中讨论,或者如果您计划,为Presto开源项目做出贡献。

集群中的协调节点和工作节点

如第2章中所述,首次安装Presto时,仅使用了一个机器运行一切。为了获得所需的可伸缩性和性能,此部署不适合。

Presto是类似于大规模并行处理的分布式SQL查询引擎MPP架构)样式的数据库和查询引擎。而不是依靠垂直缩放在运行Presto的服务器上,它能够将所有处理分散在服务器以横向方式。这意味着您可以添加更多节点以获得更多的处理能力。

利用此架构,Presto查询引擎能够处理SQL查询跨计算机集群或节点并行处理大量数据。普雷斯托43在每个节点上作为单服务器进程运行。运行Presto的多个节点,其中配置为彼此协作,组成一个Presto集群。

图4-1显示了由一个协调器和多个工作程序节点组成的Presto群集的高级概述。

Presto用户通过以下方式连接到协调器:客户端,例如使用JDBC驱动程序或Presto CLI的工具。然后,协调节点与workers协作,workers访问数据源。

图4-1。协调节点和workers的Presto体系结构概述协调器是Presto服务器,用于处理传入的查询并管理工作-er执行查询。工作者是Presto服务器,负责执行任务和处理数据。发现服务通常在协调器上运行,并允许workers进行注册参加集群。客户,协调节点和workers之间的所有在这里插入图片描述
通信和数据传输都使用通过HTTP / HTTPS的基于REST的交互

图4-2显示了集群之间的通信是如何发生的。

在这里插入图片描述

协调点与workers通信以分配工作,更新状态并获取顶级结果集返回给用户。workers互相交谈以从上游任务中获取数据,在其他worker上运行。workers从数据源检索结果集。

图4-2。 Presto集群中协调节点与workers之间的通信

协调节点

Presto协调器是负责从以下服务器接收SQL语句的服务器用户,解析这些语句,计划查询并管理工作程序节点。这是Presto安装的大脑,也是客户端连接到的节点。

用户数通过Presto CLI,使用JDBC的应用程序与协调器进行交互或ODBC驱动程序或其他适用于多种语言的客户端库。这协调器接受来自客户端的SQL语句,例如SELECT查询执行。每个Presto安装都必须在一名或多名worker旁边设有协调节点。为了出于开发或测试目的,可以将Presto的单个实例配置为执行这两个角色。

协调器跟踪每个workers上的活动并协调查询的执行。协调器创建涉及一系列阶段的查询的逻辑模型。图4-3显示了客户,协调者和workers之间的通信。接收到SQL语句后,协调器将负责解析,分析,计划和安排在Presto worker节点上执行的查询。该语句被翻译为在以下群集上运行的一系列关联任务workers。

当workers处理数据时,协调节点将检索结果并在输出缓冲区上暴露给客户端。一旦输出缓冲区完全完成在客户读取后,协调器代表代表向workers请求更多数据客户端。反过来,workers与数据源进行交互以从中获取数据他们。结果,客户端不断请求数据并由客户端提供数据。从数据源开始直到查询执行完成为止。

协调节点通过使用基于HTTP的方式与workers和客户端进行通信协议。
在这里插入图片描述

图4-3。客户端,协调器和workers通信处理SQL语句

Discovery Service 发现服务

Presto使用发现服务来查找群集中的所有节点。

每个Presto实例在启动时向发现服务注册并定期发送心跳信号。这允许协调器拥有可用工作程序的最新列表,并将该列表用于计划查询执行。如果workers未能报告心跳信号,则发现服务将触发故障检测器,并且该workers将无资格执行进一步的任务。为了简化部署并避免运行其他服务,Presto协调器通常运行发现服务的嵌入式版本。它与Presto共享HTTP服务器,因此使用相同的端口。

因此,发现服务的工作程序配置通常指向主机协调节点的IP和端口。

workers

Presto worker是Presto安装中的服务器。它负责执行任务由协调节点分配并用于处理数据。工作节点从中获取数据数据源,方法是使用连接器,然后与每个数据源交换中间数据其他。最终得到的数据将传递给协调器。协调点是负责收集worker的结果并提供最终结果给客户。

在安装过程中,workers被配置为知道主机的主机名或IP地址。群集的发现服务。worker启动时,会向发现服务,可将其提供给协调器以执行任务。workers使用基于HTTP的协议与其他workers和协调节点进行通信。

图4-4显示了多个workers如何从数据源和列中检索数据努力处理数据,直到一名worker可以将数据提供给协调节点。
在这里插入图片描述

图4-4。集群中的workers协作以处理SQL语句和数据基于连接器的

Connector-Based Architecture 连接器基础架构

Presto存储与计算分离的核心是基于连接器的体系结构。

连接器为Presto提供了访问任意接口的接口数据源。每个连接器在基础数据源上提供基于表的抽象。只要可以使用Presto可用的数据类型,可以创建连接器,并且查询引擎可以使用数据进行查询处理。

Presto提供了服务提供商接口(SPI),这是一种API,用于实现一个连接器。通过在连接器中实现SPI,Presto可以使用标准的内部执行dard操作以连接到任何数据源并在以下位置执行操作任何数据源。

连接器负责与特定数据有关的细节来源。每个连接器都实现API的三个部分:

•获取表/视图/模式元数据的操作

•产生逻辑分区的数据单元的操作,以便Presto可以并行进行读取和写入

•数据源和接收器,用于将源数据与查询引擎期望的内存格式进行相互转换。

Presto为HDFS / Hive,MySQL,PostgreSQL,MS SQL Server,Kafka,Cassandra,Redis等系统提供了许多连接器。

在第6章和在第7章中,您将了解几个连接器。可用连接器列表持续增长。有关支持的连接器的最新列表,请参阅第13页“文档”中所述的Presto文档。Presto的SPI还使您能够创建自己的自定义连接器。这可能如果您需要访问没有兼容连接器的数据源,则需要。如果你最后创建一个连接器,我们强烈建议您了解有关Presto开源社区的更多信息,使用我们的帮助并贡献您的连接器。

查看有关更多信息,请参见第12页“ Presto资源”。自定义连接器可能如果您的组织内有唯一或专有的数据源,则也需要。这就是Presto用户可以通过使用SQL查询任何数据源的真正方法SQL-on-Anything。

图4-5显示了Presto SPI如何包含用于元数据,数据的单独接口统计信息和协调器使用的数据位置,以及供协调器使用的数据流worker。图4-5。
在这里插入图片描述

SPI

**Presto服务提供商接口(SPI)**概述Presto连接器是每个服务器在启动时加载的插件。它们已配置通过目录属性文件中的特定参数并从插件加载目录。我们将在第6章中对此进行更多探讨。

  • Presto在其许多方面都使用了基于插件的架构功能。除了连接器之外,插件还可以提供事件侦听器,访问控制以及功能和类型的实现。提供者。

Catalogs, Schemas, and Tables目录,架构和表

Presto群集使用基于连接器的体系结构处理所有查询如前所述。

每个目录配置都使用连接器来访问特定数据来源。数据来源在目录中公开一个或多个模式。每个模式包含使用不同数据在表行中提供数据的表和带有列的表类型。您可以在第8章中找到有关目录,模式,表等的更多信息。

特别是在第136页的“目录”,第137页的“模式”和第136页的“表”中139。

查询执行模型

现在您已经了解了Presto的任何实际部署如何涉及集群在协调节点和许多workers的陪伴下,我们可以看看如何处理实际的SQL查询语句。

  • 查阅第8章和第9章,以了解有关SQL支持的详细信息的Presto。

了解执行模型可以为您调整Presto针对特定查询的性能提供必要的基础知识。

回想一下,协调器从CLI接受最终用户的SQL语句使用ODBC或JDBC驱动程序或其他客户端库的软件。协调人然后触发workers从数据源获取所有数据,创建结果数据集,并将其提供给客户端。让我们先仔细研究一下协调器内部发生的情况。

当一个SQL陈述式提交给协调节点,以文本格式接收。协调节点获取该文本,然后对其进行分析和分析。然后创建一个执行计划在Presto中使用内部数据结构称为查询计划。

显示此流程在图4-6中。查询计划大致表示处理数据所需的步骤并根据SQL语句返回结果。

图4-6。处理SQL查询语句以创建查询计划如图4-7所示,查询计划生成使用元数据SPI和数据统计SPI创建查询计划。因此协调器使用SPI收集有关直接连接到数据源的表和其他元数据的信息。
在这里插入图片描述

图4-7。服务提供商接口用于查询计划和调度协调器使用元数据SPI来获取有关表,列和类型。这些用于验证查询在语义上是否有效,并用于执行在原始查询和安全性检查中对表达式进行类型检查。统计信息SPI用于获取有关行数和表大小的信息,以用于在计划过程中执行基于成本的查询优化。
在这里插入图片描述

然后,可以在创建分布式查询计划时促进数据位置SPI。它用于生成表内容的逻辑拆分。分割是的最小单位工作分配和并行性。

  • 不同的SPI更具概念上的分离;实际上下层Java API由不同的Java包分隔,更细粒度的方式

分布式查询计划是对简单查询计划的扩展,包括一个或更多阶段。简单查询计划分为计划片段。阶段是计划片段的运行时化身,它包含工作的所有任务由阶段的计划片段描述。协调节点分解了计划,以允许在集群上进行处理,从而使workers可以并行执行,从而加快了整体查询的速度。一个以上的阶段会导致创建阶段的依赖关系树。阶段数取决于查询的复杂性。例如,查询表,返回的列,JOIN语句,条件,GROUP BY操作和其他SQL语句都会影响创建的阶段数。

图4-8显示了如何将逻辑查询计划转换为分布式查询规划集群中的协调器。
在这里插入图片描述

图4-8。将查询计划转换为分布式查询计划分布式查询计划定义了查询的阶段和执行方式。Presto群集。协调节点使用它来进一步计划和安排任务worker。一个阶段包含一个或多个任务。通常,涉及许多任务,每个任务处理一部分数据。协调节点将阶段性任务分配给集群中的workers,如下所示:如图4-9所示。

图4-9。协调节点执行的任务管理任务处理的数据单位称为splits(分片)。在这里插入图片描述
splits是workers可以检索和处理的基础数据段的描述符。它是并行度和工作分配的单位。连接器对数据执行的特定操作取决于基础数据源。例如,Hive连接器以文件路径的形式描述splits,其中包含偏移量和长度,指示需要处理文件的哪一部分。source阶段的任务以分页的形式生成数据,这些页面是以下内容的集合:列格式的行。这些页面流向其他中间下游阶段。页面由交换算子在各个阶段之间转移,该算子从中读取数据上游阶段的任务。

数据源SPI以从基础数据源获取数据在连接器的帮助下。该数据将呈现给Presto,并流经网页形式的引擎。算子根据他们的要求处理和生产页面语义。例如,过滤器会删除行,投影会产生包含新内容的页面派生列,依此类推。任务中的运算符序列称为管道。管道的最后一个运算符通常将其输出页面放在任务的外部-放置缓冲区。下游任务中的Exchange算子会消耗来自上游任务的输出缓冲区。所有这些操作并行发生在不同的位置worker,如图4-10所示。

在这里插入图片描述

splits中的数据在任务之间传输并不同地处理worker因此,任务是分配给worker时计划片段的运行时化身。创建任务后,它将为每个splits实例化驱动程序。每个驱动程序都是运算符流水线的实例,并在splits中执行数据处理。一种任务可能会使用一个或多个驱动程序,具体取决于Presto的配置和环境,如图4-11所示。
一旦所有驱动程序完成,并且数据是传递到下一个splits后,驱动程序和任务已完成工作后被销毁。
在这里插入图片描述
图4-11。具有输入和输出splits的任务中的并行驱动程序

算子处理输入数据以为下游算子产生输出数据。示例运算符是表扫描,过滤器,联接和聚合。这些系列算子形成算子管道。例如,您可能有一个首先扫描并读取数据,然后对数据进行筛选,最后对数据进行部分聚合。要处理查询,协调器会使用来自以下位置的元数据创建splits列表连接器。使用splits列表,协调器开始在worker收集splits中的数据。在查询执行期间,协调器跟踪所有可用于处理的splits以及运行任务的位置worker和加工分裂。随着任务完成处理并产生更多splits以进行下游处理,协调器继续安排任务,直到没有剩余splits要处理。在worker上处理所有splits之后,所有数据均获得,并且协调节点可以将结果提供给客户

Query Planning

在深入探讨Presto查询计划程序和基于成本的优化如何工作之前,让我们建立一个阶段,在特定背景下构想我们的考虑因素。 我们提出一个示例查询作为我们探索的背景,可帮助您了解以下过程查询计划。
例4-1使用TPC-H数据集-请参见“ Presto TPC-H和TPC-DS连接器”在第92页上-汇总每个国家/地区的所有订单的价值并列出排名前五的国家

在这里插入图片描述
让我们尝试了解查询中使用的SQL构造及其用途:
•在FROM子句中使用三个表的SELECT查询,隐式定义CROSS在国家,订单和客户表之间进行联接
•在何处保留来自国家,订单和客户表
•使用GROUP BY regionkey进行汇总以汇总每个订单的订单值国家
•子查询(从区域WHERE regionkey = n.regionkey中选择名称)从区域表中提取区域名称; 请注意,此查询是相关的,例如如果应该针对包含的每一行独立执行结果集
•一个排序定义,ORDER BYorders_sum DESC,用于对结果进行排序返回
•定义了五行限制,仅返回最高订单金额的国家/地区并过滤掉所有其他

Parsing and Analysis

在计划查询执行之前,需要先对其进行分析和分析。可以找到有关SQL的详细信息以及用于构建查询的相关语法规则
在第8章和第9章中进行介绍时,Presto会在解析时验证这些语法规则的文本。下一步,Presto分析查询:识别查询中使用的表
表是按目录和架构组织的,因此多个表可以具有一样的名字。例如,TPC-H数据在sf10.orders,sf100.orders等不同的架构。
识别查询中使用的列合格的列引用orders.totalprice明确引用到订单表中的totalprice列。通常,但是,SQL查询仅按名称引用列-总价,如示例4-1所示。Presto分析器可以确定列源自哪个表。标识对ROW值内字段的引用取消引用表达式c.bonus可能引用表中的Bonus列命名为c或别名为c。或者,它可以引用行的c列中的奖励字段类型(具有命名字段的结构)。决定Presto的分析仪的工作这是适用的,其中表限定的列引用优先于含糊不清的情况。分析需要遵循SQL语言的范围和可见性规则。稍后使用收集到的信息(例如标识符消除歧义)在计划过程中,因此计划者无需了解查询语言量规范围规则再次出现。如您所见,查询分析器具有复杂且跨领域的职责。它的作用是非常有技术含量,并且只要用户查询,就从用户的角度来看它仍然是不可见的正确的。每当查询违反SQL语言时,分析器就会显示其存在规则,超出用户权限或由于其他某些原因不健全。
分析完查询并处理并解析了查询中的所有标识符后,Presto进入下一阶段,即查询计划

Initial Query Planning

可以将查询计划视为产生查询结果的程序。 回想一下SQL是一种声明性语言:用户编写一个SQL查询以指定他们想要的数据从系统。 与命令式程序不同,用户不指定如何处理数据以获得结果。 这部分留给查询计划者和优化器确定处理数据以获得所需结果的步骤顺序。这一系列步骤通常称为查询计划。 从理论上讲一定数量的查询计划可以产生相同的查询结果。
性能有很大的不同,这就是Presto计划器和优化器试图确定最优计划的地方。 总是产生t的计划同样的结果被称为等效计划。 让我们考虑前面在示例4-1中显示的查询。 这个查询最简单的查询计划是最类似于的查询计划的SQL句法的结构。 本方案如例4-2所示。 为了这个铁饼的目的,清单应该是不言自明的。 你只需要知道计划是一棵树,它执行从叶节点开始,并
在这里插入图片描述

沿着树结构进行。
在这里插入图片描述

查询计划中的每个元素都可以直接实现,而且势在必行时尚。 例如,TableScan访问其基础存储中的表并返回一个包含表中所有行的结果集。 过滤器接收行并应用过滤每个条件,仅保留满足条件的行。 crossjoin对从其子节点接收的两个数据集进行操作。 它产生所有这些数据集中的行的组合,可能将其中一个数据集存储在内存,因此不必多次访问底层存储。

  • Presto的最新发行版更改了不同操作的命名在查询计划中。 例如,TableScan等同于ScanProject具有表规范的项目。 筛选操作已重命名为FilterProject。

但是,提出的想法保持不变。现在,让我们考虑一下该查询计划的计算复杂性。 不知道考虑到实际实施的所有细节,我们无法完全推理关于复杂性。 但是,我们可以假设查询计划节点的大小是它产生的数据集的大小。 因此,我们描述大欧米茄(Big Omega)表示法的复杂性,它描述了渐近线渐近下查询计划边界。如果N,O,C和R代表国家,订单,客户,和区域表,我们可以分别观察以下内容:
•TableScan [orders]读取orders表,返回O行,因此其复杂度为Ω(O)。类似地,其他两个TableScan返回N和C行。因此他们的组合复杂度分别为Ω(N)和Ω(C)。
•TableScan [nation]和TableScan [orders]上方的CrossJoin结合了来自国家和订单表的数据;因此,其复杂度为Ω(N×O)。
•上面的CrossJoin结合了较早的CrossJoin,产生了N×O行,使用TableScan [customer],因此使用Customer表中的数据,因此其复杂度为Ω(N×O×C)。
•底部的TableScan [region]具有复杂度Ω(R)。但是,由于LateralJoin,它被调用N次,其中N为从中返回的行数聚合。因此,总的来说,该运算会产生Ω(R×N)成本。
•排序操作需要对一组N行进行排序,因此不会花费更少的时间比正比于N×log(N)。暂时忽略其他操作的费用不比我们的操作高

到目前为止,先前计划的总成本至少为Ω[N + O + C +(N×O)+(N×O×C)+(R×N)+(N×log(N))]。在不知道相对表格大小的情况下,可以简化为Ω[(N×O×C)+(R×N)+(N×log(N))]。添加原因有能力的假设是,区域是最小的表格,而国家是第二个最小的表格,
我们可以忽略结果的第二部分和第三部分,并得到简化的结果Ω(N×O×C)。

足够的代数公式。现在是时候看看这在实践中意味着什么了!让我们来提供了一个受欢迎的购物网站的示例,该网站拥有200个用户的1亿客户总共下达了10亿个订单的国家/地区。这两个表的CrossJoin需要实现20亿(20,000,000,000,000,000,000)行。适度强大的100节点集群,每个节点每秒处理100万行,花费了63个多世纪的时间来为我们的查询计算中间数据。

当然,Presto甚至不会尝试执行这样的幼稚计划。天真的计划有其作用,虽然。最初的计划是两个世界之间的桥梁:SQL的世界语言及其语义规则以及查询优化领域。查询优化的作用是将初始计划转化为同等计划

在给定的条件下,至少可以在合理的时间内尽快执行Presto群集的有限资源。让我们谈谈查询优化是如何进行的达到这个目标。

Optimization Rules

在本节中,您将了解许多重要的优化中的一些Presto中实施的规则。

谓词下推

谓词下推可能是唯一最重要的优化,并且最容易实现理解。 它的作用是将过滤条件移到尽可能靠近数据源的位置尽可能。 结果,在查询执行期间尽早进行数据缩减。 在我们的例子中,它将一个Filter转换为一个更简单的Filter和一个InnerJoin基于相同的CrossJoin条件,得出了示例4-3中所示的计划。出于可读性考虑,计划中未更改的部分被排除在外
在这里插入图片描述
现在,存在的“更大”联接在相等时转换为InnerJoin等价条件。 在此不赘述,现在假设这样的联接可以是在分布式系统中有效地实现,计算复杂度相等产生的行数。 这意味着谓词下推取代了“至少”Ω(N×O×C)交叉连接,其连接“恰好”为Θ(N×O)。但是,谓词下推不能改善国家与国家之间的CrossJoin订购表,因为没有中间条件连接这些表。 这消除了交叉连接起作用的地方。

Cross Join Elimination

在没有基于成本的优化器的情况下,Presto会将包含在SELECT查询按其在查询文本中出现的顺序。一个重要的当要联接的表没有联接条件时,会发生这种情况的例外,这导致交叉连接。在几乎所有实际情况下,都不需要交叉连接,并且稍后会滤除所有相乘的行,但是交叉联接本身有很多工作为此,它可能永远无法完成。消除交叉联接可对要联接的表进行重新排序,以最大程度地减少表的数量。交叉联接,理想情况下将其减小为零。在没有有关亲戚的信息表大小,除了消除交叉联接外,还保留了表联接顺序,因此用户仍然可以控制。消除交叉联接对我们的示例查询的影响,可以在示例4-4中看到。现在,两个连接都是内部连接,带来了整体计算连接到Θ(C + O)=Θ(O)的实际成本。查询计划的其他部分没有自初始计划以来发生了变化,因此总体查询计算成本至少为Ω[O +(R×N)+(N×log(N))]-当然,O分量代表订单表中的行是主要因素。
在这里插入图片描述

TopN

通常,当查询具有LIMIT子句时,它前面是ORDER BY子句。如果没有排序,SQL不能保证返回哪些结果行。这
我们的查询中还包含ORDER BY和LIMIT的组合。
当执行这样的查询时,Presto可以对所有产生的行进行排序,然后只保留其中的前几个。这种方法将具有Θ(row_count×lo(row_count)的计算复杂度和Θ(row_count)的内存占用量。但是,这并不是最佳选择,并且仅对整个结果进行排序会浪费很多时间。排序结果的较小子集。因此,优化规则会滚动ORDER BY后跟LIMIT进入TopN计划节点。在查询执行期间,TopN保持堆数据结构中所需的行数,在读取时更新堆以流方式输入数据。这将计算复杂度降低到Θ(row_count×log(limit)),内存占用量为Θ(limit)。总体查询计算成本现在为Ω[O +(R×N)+ N]。

Partial Aggregations 预聚合

Presto不需要将订单表中的所有行传递给join,因为我们对单个订单不感兴趣。 我们的示例查询计算一个聚合,即每个n的总价格之和。因此,可以预先聚合行,如示例4-5所示。 我们通过聚合数据来减少流入下游连接的数据量。结果不完整,这是我的这就是为什么这被称为预聚合。 但是数据量可能会减少,这将显著提高查询性能。
在这里插入图片描述
为了改进并行性,这种预聚合被不同地实现,称为部分聚合。 在这里,我们提出了简化的计划,但在实际的EXPLAIN计划中,这是其表示方式不同于最终的聚合方式
如示例4-5所示,这种预聚合并不总是一种提升。 当部分聚合不会减少数据量时,这会对查询性能产生不利影响。 因此,默认情况下,优化当前处于禁用状态,并且可以通过push_partial_aggregation_through_join会话切换启用。 默认情况下,Presto使用部分聚合和放置它们在连接上方,以减少通过Presto节点之间的网络。 对于这些部分聚合,我们将需要考虑非简化的查询计划。

实施规则

到目前为止,我们涵盖的规则是优化规则,即旨在减少查询处理时间,查询的内存占用量或交换的数据量通过网络。 但是,即使在我们的示例查询中,初始计划包含一个根本没有实现的操作:横向连接。 在下一秒我们来看一下Presto如何处理这类操作。

Lateral Join Decorrelation 横向连接解耦

横向联接可以实现为遍历一个对象的所有行的for-each循环。数据集,并对每个数据集执行另一个查询。 这样的实现是可能的,但这不是Presto处理像我们的示例这样的案例的方式。 相反,Presto对子查询进行解耦,提取所有相关条件并形成规则left join。 用SQL术语来说,这对应于查询的转换:
在这里插入图片描述
即使我们可以互换使用这些构造,但谨慎的读者还是很熟悉使用SQL语义的人立即意识到它们并不完全等效。 首先当它们是具有相同区域的区域表中的重复条目时,查询将失败键,而第二个查询不会失败。 相反,它将产生更多结果行。因此,横向连接解耦除了使用
加入。 首先,它对所有源行进行**“编号”,以便对其进行区分。 第二,连接之后,它将检查是否有任何行重复**,如示例4-6所示。 如果检测到重复项,查询处理失败,以保留原始查询语义。
在这里插入图片描述

Semi-Join (IN) Decorrelation

在横向连接示例中,还可以使用IN谓词过滤行。 实际上,一个IN谓词可以在过滤器(WHERE子句)或投影(SELECT(选择)子句)。 当您在投影中使用IN时,很明显这不是简单的布尔值运算符,例如EXISTS。 相反,IN谓词可以评估为true,false或null
让我们考虑一个查询,该查询旨在查找客户和项目供应商来自同一国家/地区的订单,如示例4-7所示。 这样的命令可能很有趣。 例如,我们可能想节省运输成本或减少运输通过直接从供应商运送到客户的方式对环境产生影响,绕过我们自己的配送中心。
在这里插入图片描述
与横向连接一样,这可以通过在外部的行上循环来实现查询,其中子查询可检索项目的所有供应商的所有国家/地区被多次调用
Presto不会执行此操作,而是对子查询进行相关处理-对子查询进行求值一次,移除相关条件,然后与外部通过使用相关条件进行查询。 棘手的部分是确保加入不会将结果行相乘(因此将使用重复数据删除聚合),并且转换正确地保留了IN谓词的三值逻辑。在这种情况下,重复数据删除聚合使用与联接相同的分区,因此它可以以流方式执行,而无需通过网络进行数据交换,并且具有最小的内存占用量

基于成本的优化器 CBO

在第53页的“查询计划”中,您了解了演示计划器如何将文本形式的查询转换为可执行和优化的查询计划。您在“选项”中了解到了各种优化规则以及它们在执行时对查询性能的重要性。您还在第60页的“执行规则”中看到了实现规则,如果没有这些规则,查询计划就不会是这样的可执行的。 我们从一开始就走了这条路径,从用户那里接收到查询文本,到最后,最后的执行计划就准备好了。 一路上,我们看到了选定的计划这一点至关重要,因为它们会使计划执行得更快一个数量级,或者会使计划完全可执行。现在,让我们来仔细看看能让他们做出决策决定的计划转换不仅基于查询的形状,而且更重要的是,基于被查询数据的形状。 这就是Presto最先进的基于成本的优化器(CBO)所做的。

成本概念

之前,我们使用示例查询作为我们的工作模型。 让我们使用类似的方法再次为方便起见并简化理解。 如例4-8所示,某些查询子句与本节无关。 这允许您将重点放在查询计划者基于成本的决策上。
在这里插入图片描述
在没有基于成本的决策的情况下,查询计划者规则会针对该查询产生一个计划,如示例4-9所示。 该计划是完全确定的通过SQL查询的词法结构。 优化器仅使用语法信息因此它有时被称为语法优化器。 这个名字的意思是幽默,突出了优化的简单性。 由于查询计划是仅基于查询,您可以通过调整查询中表的句法顺序。
在这里插入图片描述
排序条件的简单更改会影响查询计划,从而影响查询性能,这一事实对于SQL分析人员来说很麻烦。然后,创建有效的查询需要内部了解Presto处理查询的方式。查询作者不应被要求具有此知识以获得最佳知识性能超出Presto。除了编写查询的人之外,诸如ApacheSuperset,Tableau,Qlik或MicroStrategy之类的工具不会编写对于Presto最佳的查询。

基于成本的优化器可确保查询的两个变体生成相同的最佳查询计划,以供Presto的执行引擎进行处理。
从时间复杂度的角度来看,例如您是否将带国家的表加入客户,或者反之亦然,将带国家的客户表加入就没有关系。
这两个表都需要处理,并且在哈希联接实现的情况下,总运行时间与输出行数成正比。但是,时间复杂度并不是唯一重要的事情。这通常适用于使用数据,但是对于大型数据库系统尤其如此。 Presto还需要关注内存使用情况和网络流量。为了说明联接的内存和网络使用情况,Presto需要更好地了解联接的实现方式。
在单查询和并发工作负载中,CPU时间,内存需求和网络带宽使用情况都是影响查询执行时间的三个维度。这些尺寸构成了Presto的成本。

Cost of the Join

在相等条件(=)上连接两个表时,Presto实现了算法的扩展版本,称为哈希联接 hash join。联接表之一称为
构建端build side。该表用于建立具有联接条件的查找哈希表列作为键。另一个连接的表称为探针端 probe side。一旦查询
哈希表已准备就绪,处理了来自探针端的行,并使用了哈希表在恒定时间内查找匹配的构建端的行。默认情况下,Presto使用三级
散列以便尽可能并行化处理:

  • 1.两个散列表都基于分布在工作节点上连接条件列的哈希值。应该匹配的行在连接条件列上具有相同的值,因此它们被分配给相同的值
    节点。这通过在此阶段使用的节点数来减少问题的大小。这个阶段,节点级别数据分配是哈希的第一级别

  • 2.在节点级别,构建侧进一步分散在构建侧工作程序中线程再次使用哈希函数。建立哈希表会占用大量CPU资源进程,使用多个线程来完成这项工作,从而大大提高了吞吐量。

  • 3.每个工作线程最终都会产生最终查找哈希的一个分区。每个分区本身就是一个哈希表。将分区合并为两级查找哈希表,这样我们就可以避免将探针侧分散到多个线程。探针端仍在多个线程中处理,但是线程分批分配工作,比通过使用哈希函数获取数据对线程进行分区要快。

如您所见,构建侧保留在内存中,以促进快速的内存数据加工,内存中的数据处理。当然,内存占用也与构建侧大小成正比。这意味着构建端必须适合可用于节点。这也意味着更少的内存可用于其他操作以及其他查询。这是与连接关联的内存成本。还有网络成本。在前面描述的算法中,两个联接表通过网络传输以促进节点级别的数据分配

基于成本的优化器可以选择哪一个表应该是构建表,从而控制连接的内存成本。在某些条件下,优化器还可以避免在网络上发送一个表,从而减少网络带宽的使用(降低网络成本)。 为了完成它的工作,基于成本的优化器需要知道连接表的大小,它提供作为表中的统计数据。

Table Statistics

在第47页的“基于连接器的体系结构”中,您了解了连接器的作用。每个表都由一个连接器提供。除了表架构信息和访问实际数据时,连接器可以提供表和列的统计信息:
•表中的行数
•列中不同值的数量
•列中的NULL值的分数
•列中的最小值和最大值
•列的平均数据大小

当然,如果缺少某些信息,例如,varchar列未知-连接器仍可以提供其他信息,并且基于成本的优化器会使用可用的资源。
估计联接表中的行数,以及可选的平均列的年龄数据大小,基于成本的优化器已经具有足够的知识在我们的示例查询中确定表的最佳顺序。 CBO可以从最大的表(lineitem)开始,然后加入其他表订单,然后是客户,然后是国家:
在这里插入图片描述

这样的计划是好的,应该考虑,因为每个联接的关系都较小。
作为构建侧,但不一定是最佳选择。如果您运行示例查询,使用提供表统计信息的连接器,可以使用会话属性启用基于成本的优化器

SET SESSION join_reordering_strategy = 'AUTOMATIC';

借助连接器提供的表格统计信息,Presto可能会提出不同的计划:
在这里插入图片描述
选择这个计划是因为它避免了在网络上发送三次最大的表(lineitem, 该表仅在节点上一次)。
最终计划取决于联接表的实际大小以及其集群中的节点数,因此如果您自己尝试,你可能会得到一个不同于这里显示的计划。
谨慎的读者会注意到,仅基于联接条件,表之间的链接以及表的数据大小(包括表的数量)来选择联接顺序。
行和每列的平均数据大小。其他统计数据对于优化至关重要,涉及更多的查询计划,其中包含表之间的中间操作扫描和联接,例如过滤器,聚合和非内部联接。

Filter Statistics

如您所见,了解查询中涉及的表的大小对于正确地重新排序查询计划中的连接表是至关重要的。但是,只知道表格大小还不够。考虑对示例查询的修改,其中用户添加了另一个条件,例如l.partkey = 638,以便在其数据集,用于获取有关特定项目的订单信息:
在这里插入图片描述
在添加条件之前,lineitem是最大的表,而查询是计划优化对该表的处理。但是现在,过滤的行项是最小的连接关系之一。
查看查询计划表明,已过滤的lineitem表现在足够小。CBO将表放在连接的构建侧,以便用作筛选器
其他表:
在这里插入图片描述
为了估算已过滤的订单项表中的行数,CBO再次使用连接器提供的统计信息:

一列中不同值的数量和列中NULL值的分数。对于partkey = 638条件,没有NULL值满足条件,因此优化器知道行数减少了用partkey列中NULL值的分数表示。此外,如果您粗略地假设列中值的均匀分布,您可以得出最终的行数:

已过滤的行=未过滤的行x(1-null 百分数) /不同值的数量
filtered rows = unfiltered rows * (1 - null fraction)/ number of distinct values

显然,只有当值的分布均匀时,公式才是正确的
但是,优化器不需要知道行数;它只需要知道它的估计,因此总体上不成问题。当然,如果某商品的购买频率比其他商品要高得多,例如,Starburst糖果—估计值可能相差太远,优化器可能选择了错误的计划。

当前,发生这种情况时,您必须禁用CBO。将来,连接器将能够提供有关数据分布的信息,以处理此类情况。例如,如果直方图可用于数据,则CBO可以更准确地估计过滤后的行。

分区表的表统计信息

值得一提的是,过滤表的一种特殊情况:分区表。数据可以组织到Hive / HDFS仓库中的分区表中通过Hive连接器访问;请参阅“用于分布式存储数据的Hive连接器源”(第93页)。当通过分区键上的条件过滤数据时,在查询执行期间仅读取匹配的分区。此外,由于表统计信息按分区存储在Hive中,CBO获取统计信息信息仅适用于已读取的分区,因此更加准确。当然,每个连接器都可以为过滤的关系提供这种改进的统计信息。我们在这里仅指的是Hive连接器提供统计信息的方式。

Join Enumeration 联接枚举

到目前为止,我们已经讨论了CBO如何利用数据统计信息与执行查询的最佳计划。特别是,它选择一个最佳联接顺序,这主要影响查询性能,主要有两个原因:

  • 哈希加入实施
    哈希联接实现是不对称的。重要的是要仔细选择哪个输入是构建侧,哪个输入是探针侧。
  • 分布式联接类型
    仔细选择广播还是重新分发数据在联接输入中非常重要

广播与分布式联接

在上一节中,您了解了哈希联接实现构建端和探测端的重要性。因为Presto是分布式系统,所以加入可以在一组worker之间并行完成,每个worker处理一个联接的一部分。为了进行分布式联接,可能需要在整个网络上分布数据,并且可以使用不同的策略,这些策略的效率取决于数据的形状。

广播加盟策略

在广播连接策略中,将连接的构建端广播给所有worker正在并行执行连接的节点。换句话说,每个联接都会为构建端获取数据的完整副本,如图4-12所示。仅当探针侧保持分布在worker之间而没有复制。否则,将创建重复的结果。
在这里插入图片描述
构建侧较小时,广播加入策略是有利的,允许具有成本效益的数据传输。当探测端非常大时,优势也更大,因为它避免了在分布式连接情况下必须重新分发数据。

分布式加入策略

在分布式连接策略中,输入到构建侧探针侧的数据在整个集群中重新分布,以便工作进程并行执行连接。
通过网络传输数据的不同之处在于,每个worker都会收到一个数据集的唯一部分,而不是副本广播加入案例。
数据重新分配必须使用分区算法,例如将匹配的联接键值发送到同一节点。例如,我们有特定节点上的以下联接键数据集:
在这里插入图片描述
通过对数据进行分区,CBO保证可以并行计算联接,而不必在处理过程中共享信息。

分布式联接一个的优点是它允许Presto计算联接,从而双方都非常大,并且一台机器上没有足够的内存来容纳整个
探针侧在内存中。缺点是需要通过网络发送额外的数据。广播联接和分布式联接策略之间的决策必须花费成本。
每个策略都有权衡取舍,我们必须按顺序考虑数据统计花费最优的一个。此外,这也需要在加入过程中确定重新排序过程。根据联接顺序和应用过滤器的位置,数据形状变化。这可能会导致两个之间的分布式联接的情况数据集在一个连接顺序方案中可能是最好的,但广播连接在以下情况下可能更好不同的情况。联接枚举算法将这一点考虑在内。

  • Presto使用的联接枚举算法相当复杂超出了本书的范围。它详细记录在Starburst博客文章。它将问题分解为以下子问题较小的分区,找到具有递归的正确联接用法,并且将结果汇总为全局结果。

使用表格统计工作

为了利用Presto中的CBO,您的数据必须具有统计信息。没有数据统计数据显示,CBO不能做太多事情;它需要数据统计信息来估计行和不同计划的代价

由于Presto不存储数据,因此为Presto生成统计信息取决于连接器的实现。在撰写本文时,Hive连接器为Presto提供了数据统计信息。其他数据源,例如关系数据库连接器,也可以提供统计信息。例如,PostgreSQL可以收集和存储其数据的统计信息。可以扩展PostgreSQL连接器的实现,以将这些统计信息提供给Presto的CBO。但是,在撰写本文时,在开源中不可用。我们希望随着时间的流逝,更多的连接器将支持统计信息,您应该继续参考Presto文档以获取最新信息。对于Hive连接器,可以使用以下方式收集统计信息:

•使用Presto的ANALYZE命令收集统计信息。
•将数据写入表时,使Presto能够收集统计信息。
•使用Hive的ANALYZE命令收集统计信息。

重要的是要注意,Presto将统计信息存储在Hive元存储中,和Hive用于存储统计信息的位置相同。因此,如果您之间共享相同的表格
Hive和Presto,他们会覆盖彼此的统计信息。这是你确定如何管理统计信息收集时要考虑到这一点。

Presto分析

Presto提供了ANALYZE命令来收集连接器的统计信息。例如,Hive连接器。运行时,Presto通过它的执行引擎使用以下方式计算列级统计信息,并将统计信息存储在Hive Metastore中。语法如下
如下:

ANALYZE table_name [WITH(property_name = expression [...]]

例如,如果您想从排期表中收集和存储统计信息,则可以运行以下命令:

ANALYZE hive.ontime.flights;

在分区的情况下,如果我们只想分析特定的分区,则可以使用WITH子句:

ANALYZE hive.ontime.flights WITH (partitions = ARRAY[ARRAY['01-01-2019']])

如果您有多个分区键,并且您想使用嵌套数组,则需要每个键都将成为下一个数组中的元素。如果要分析多个分区,则使用最顶层的数组。在Presto中,指定分区的功能非常有用。例如,您可能有某种类型的ETL流程会创建新的分区。作为引入新数据后,统计信息可能会过时,因为它们不会合并新数据。但是,通过更新新分区的统计信息,您不会必须重新分析所有先前的数据。

写入磁盘时收集统计信息

如果您有始终通过Presto为其写入数据的表,则可以进行统计在写操作期间收集。例如,如果您运行CREATE TABLE AS或INSERT SELECT查询,Presto在将数据写入磁盘时收集统计信息(例如HDFS或S3),然后将统计信息存储在Hive元存储中

这是一项有用的功能,因为它不需要您运行ANALYZE的手动步骤。统计数据永远不会过时。但是,为了使其正常工作并按预期工作,表中的数据必须始终由Presto写入。此过程的开销已进行了广泛的基准测试和测试,并且对性能的影响可忽略不计。要启用此功能,可以使用Hive连接器将以下属性添加到目录属性文件中:

hive.collect-column-statistics-on-write = true

Hive ANALYZE

在Presto之外,您仍然可以使用Hive ANALYZE命令来收集统计信息对于Presto。统计信息的计算由Hive执行引擎而不是Presto执行引擎,因此结果可能会有所不同,并且存在使用Hive生成的统计信息时,始终存在与Presto行为不同的风险。通常建议使用Presto收集统计信息。例如,如果数据作为更复杂管道的一部分落地,并与可能希望使用统计数据的其他工具共享。 要使用Hive收集统计数据,可以运行以下命令:

hive> ANALYZE TABLE hive.ontime.flights COMPUTE STATISTICS;
hive> ANALYZE TABLE hive.ontime.flights COMPUTE STATISTICS FOR COLUMNS;

有关Hive ANALYZE命令的完整信息,您可以参考官方的Hive文档。

Displaying Table Statistics 显示表统计信息

收集统计信息后,查看它们通常很有用。您可能要执行此操作以确认已收集统计信息,或者您可能正在调试性能问题,并希望查看正在使用的统计信息。Presto提供了一个SHOW STATS命令:

SHOW STATS FOR hive.ontime.flights;

另外,如果您想查看数据子集的统计信息,则可以提供过滤条件。例如:

SHOW STATS FOR (SELECT * FROM hive.ontime.flights WHERE year > 2010);

结论

现在,您将了解Presto架构,并有一个协调员来接收用户请求,然后使用工作程序从数据源组装所有数据。
每个查询都在多个阶段转换为任务的分布式查询计划。数据由连接器拆分返回,并分多个阶段处理,直到最终结果是可用的,并由协调员提供给用户。如果您对Presto架构更感兴趣,可以深入了解Presto创作者撰写的论文“ Presto:SQL on Everything”,在IEEE上发布国际数据工程大会(ICDE)并在网站上提供;请参阅第12页“网站”。接下来,您将在第5章中学习有关部署Presto集群的更多信息。在第6章和第7章中使用不同的连接器连接更多数据源,以及在第8章中编写强大的查询。

猜你喜欢

转载自blog.csdn.net/weixin_45091011/article/details/114289368