Presto简介

Presto是什么?

Presto是一个facebook开源的分布式SQL查询引擎,适用于交互式分析查询,数据量支持GB到PB字节。presto的架构由关系型数据库的架构演化而来。因此Presto集群的硬件必须满足大内存,万兆网络和高计算能力特点。

Presto特点

Presto的基本概念

  1. Presto 服务进程
    Presto 集群中一共有两种服务器进程: Coordinator服务进程,Worker服务进程。因此Presto集群是Master-Slave的拓扑结构。

    • Coordinator进程: 接受查询请求,解析查询语句,生成查询执行计划,任务调度,Worker管理。
    • Worker进程: 执行被分解后端而查询执行任务–>Task
  2. Presto模型
    • Connector Presto访问不同数据源的驱动程序。每种Connector都实现Presto中标准SPI接口。当年需要使用某种Connector访问特定的数据源时,需要在$PRESTO_HOME/etc/catalog中配置文件:example.properties,并在配置文件中设置一个属性:connector.name,Presto中Connector Manager就是通过该配置属性来决定使用哪一个Connector去访问数据。
    • Catalog 对应某一类数据源,例如hive的数据,或mysql的数据。当你访问Catalog中某个表时,该表的全名总是以Catalog的名字开始。例如 名字为example.schema1.table1的表,指的是表table1位于名schema1下的schema中,而schema1又位于example的Catalog中。
    • Schema 对应mysql中的数据库
    • Table 对应mysql中的表
  3. 硬件架构
    硬件架构
  4. 软件架构
    presto软件架构

    Presto查询步骤

    1)客户端通过Http协议发送一个查询语句给Presto集群的Coordinator
    2) Coordinator街道客户端传递过来的查询语句,会对该查询语句进行解析,生成查询执行计划,并根据查询执行计划一次生成SqlQueryExecution,SqlStageExecution,HttpRemoteTask。Coordinator会根据数据本地行生成对应的HttpRemoteTask。
    3) Coordiantor将每一个Task都分发到其所需要处理的数据所在的Worker上进行执行。这个过程是通过HttpRemoteTask中的HttpClient将创建或者更新Task请求发送给数据所在节点上TaskResource所提供的RestFul接口,TaskResource接收到请求之后最终会在对应的Worker上启动一个SqlTaskExecution对象或者更新对应的SqlTaskExecution对象需要处理的Split。
    4)执行处于上有的Source Stage中的Task,这些Task通过各种Connector从相应的数据源中读取所需要的数据。
    5) 处于下游的会读取上有Stage产生的输出结果,并在该Stage每隔Task所在Worker的内存中进行后续的计算和处理。
    6)Coordinator从分发的Task之后,就会一直持续不断的从Single Stage中的Task获取计算结果,并将计算结果缓存到Buffer中,直到所有的计算结束。
    7)Client从提交查询语句之后,就会不断地从Coordinator中获取本次查询的计算结果,直到获得了所有的计算记过。并不是等到所有的查询结果都产生完毕之后一次全部显示出来,而是每产生一部分,就会显示一部分,直到所有的查询结果都显示完毕。

Presto and Trino

Presto 以前是 PrestoDB,而 Trino 以前是 PrestoSQL。
PrestoDB 于 2012 年在 Facebook 创建,直到 2019 年 1 月它们都是同一个项目。
据我所知,该项目最初是为了解决 300 PB Hive 数据仓库上的缓慢查询而创建的。还有来自 Teradata 的 Presto 发行版等等。

Trino

  • 虽然 Trino 是针对多个数据源运行 ETL 查询的公司的出色解决方案,但它还提供了节省资源和收集更多输出的选项。
  • Trino 还擅长允许用户使用 SQL 运行即席查询,而不管数据位于何处。它消除了将数据 ETL 到另一个系统的需要。
  • Trino 允许用户创建个性化的报告和统一的仪表板,以更好地查询多个数据源。

Presto

Trino 中不存在的 Presto 功能,例如 Presto-on-Spark 看起来一直是开发重点:

  • Project Aria – 非常适合处理 ORC 等文件格式。
  • Project Presto Unlimited – 一种用于创建临时内存存储桶的内存节省功能。
  • 其他用户定义的函数,例如动态 SQL 函数支持。
  • Presto-on-Spark – Spark 执行器中的库。

差异

  • Presto 已在 Facebook、Uber 和 Twitter 上大规模开发、测试和运行
  • 今天的“Presto on Spark”可以运行大量批量 ETL 作业。
  • Presto 的 RaptorX 提供多级缓存
  • Presto 社区正在通过多个协调器而不是一个协调器节点使 Presto 更加可靠和可扩展(以防止单点故障)。

Trino 和 Presto 在架构和主要功能级别上基于事实的差异。两个项目从2019年开始分开,两者的发展和侧重点不同。如何回答以下三个问题:

  1. 在哪些场景中每个更有用?(或者 – 每个项目的重点是什么?)
  2. 两者中的任何一个都需要更多的内存资源吗?
  3. 对于某些用例,两者中的任何一个是否往往更快?

TL;DR:亲自尝试 Presto 和 Trino,并尝试与这两个社区进行互动。如果你想要一个黑白的答案,我的选择是 Trino。

这是我必须让您开始使用 Trino 的存储库。https://github.com/bitsondatadev/trino-getting-started

完全公开,我是 Starburst(企业 Trino)和 Trino/PrestoDB 贡献者的开发倡导者。任何有足够经验来回答这个问题的人都是有偏见的,所以我将提供一些背景和我对公正建议的最佳尝试。我还应该提到,你在那里发布的一个来源只是写关于 Presto 和 Trino 的文章,因为这是一个热门话题,他们只想获得 SEO 和页面浏览量。他们的技术与 Presto/Trino 几乎没有任何关系,他们不会检查他们写的任何东西。所以这增加了更多的混乱。我将首先回答您的问题,然后对您在上面指出的功能做出一些答复。

  • 在哪些场景中每个更有用?

它们通常用于取代 Hive 作为日期湖查询引擎,以及跨多个数据源联合查询。每个人都有自拆分以来添加的功能。您在上面为 Presto 和下面指出的两个博客详细介绍了自拆分以来 Trino 中添加的内容:

https://trino.io/blog/2020/01/01/2019-summary.html
https://trino.io/blog/2021/01/08/2020-review.html
Trino 现在对高精度时间戳支持进行了重大修复,这对于金融科技、游戏和其他需要高粒度时间支持的行业非常有用。Trino 还支持高级 SQL 功能,例如Windows 上的WINDOW语法和模式匹配以及通过MATCH_RECOGNIZE. Trino 中还有更多的动态过滤 + 动态分区修剪支持,它通过在优化器中提示哪些分区确实需要包含在查询连接中来删除冗余扫描。

我从 Presto 看到的唯一一件大事是我个人觉得有趣的 Presto on Spark 作品。目前值得注意,但我还没有看到有人写过他们如何使用或当您可以直接访问 Spark 时该功能实际上有多大帮助。我认为理论上你会在 Presto 生态系统中获得容错能力,但查询长时间运行的 Spark 作业有点违背 Presto 的目的,即提供更快的交互式查询。

  • 两者中的任何一个都需要更多的内存资源吗?

总的来说,这几乎是一样的。肯定会有所不同,尤其是取决于您运行的查询类型和您点击的数据源。这些项目共有6年的历史。架构中的任何一方都没有发生如此根本性的变化,以至于除了可能已明确优化的例外情况外,您将体验到内存使用方面的很大差异。

  • 对于某些用例,两者中的任何一个是否往往更快?

任何人都可以争辩说他们有更好的表现,并且双方都有自己的基准表明他们更好。这是一个愚蠢的游戏,不幸的是让您感到困惑而不是帮助。对于性能问题,我始终建议您将自己与实际用例(不仅仅是 TPCH/DS)进行比较。我很乐意在这里帮助你。我能说的最好的是这两个项目都专注于加速。

Presto 在 Raptor 和这个 Spark 插件上做了很多工作。Trino 在数据湖的 Hive 和 Iceberg 连接器上投入了大量精力。如果您需要支持亚秒级延迟,Trino 还具有用于实时连接 Pinot、Druid 和 Clickhouse 的连接器。Presto 有 Pinot 和 Druid,但还没有 ClickHouse。

  • 为什么我选择 Trino 并且现在是这个社区的开发者倡导者:

在之前的一家公司拆分为 PrestoDB 和 PrestoSQL/Trino 之前,我开始使用 Presto,我们决定迁移到 Trino 主要是因为 Presto 的创建者,大多数代码贡献者(来自 Qubole 和 Teradata 以及其他公司的一群人)迁移了以及。(参见:PrestoDB贡献者图|特里诺贡献者图,你会发现大量的杰出贡献者停止围绕2019年继续在特里诺)。正如您从 Trino 公告博客中的图表中看到的那样,PrestoSQL 中正在进行更多活动。

双方都添加了一些功能,但大多数添加到 PrestoDB 的功能只会使 Facebook 受益,而不是整个社区。有很多社区贡献的 PR 已经过时了,因为它们没有被优先考虑。

最后,除了仅仅因为性能而选择 Trino 之外,我敦促您加入 Trino 和 Presto slack 频道,看看那里的活动。当您需要帮助设置某些东西,或者想要找到学习资源,或者想要通过将您的用例贡献给 Trino 社区来参与其中时,您会看到与 Presto 相比,Trino 方面很热闹。当您需要启动项目时,这将使您启动并运行。

flink Window Assigners 窗口

flink Window Assigners是Flink流处理中用于将数据流划分为有限大小的时间窗口的机制之一。Window Assigners负责将数据流中的数据分配到不同的时间窗口中。时间窗口可以基于不同的标准进行划分,如时间戳、事件数量或其他自定义标准。

Flink提供了多个Window Assigners实现,包括滚动窗口(Tumbling Windows)、滑动窗口(Sliding Windows)、会话窗口(Session Windows)等。滚动窗口是基于固定大小的时间段来划分数据流的,而滑动窗口则是根据固定大小和滑动间隔来划分数据流的。会话窗口根据一定的时间间隔来划分数据流,并根据数据之间的间隔来决定何时关闭会话窗口。

使用Window Assigners,可以将数据流中的数据分配到不同的时间窗口中,从而方便地进行聚合、计算和分析。

一、窗口(Window)

窗口(Window)是处理无界流的关键所在。窗口可以将数据流装入大小有限的“桶”中,再对每个“桶”加以处理。

1. 滚动窗口(Tumbling Windows)

滚动窗口有固定的大小,是一种对数据进行均匀切片的划分方式。窗口之间没有重叠,也不会有间隔,是“首尾相接”的状态。滚动窗口可以基于时间定义,也可以基于数据个数定义;需要的参数只有一个,就是窗口的大小(window size)。

Tumbling Windows

2. 滑动窗口(Sliding Windows)

与滚动窗口类似,滑动窗口的大小也是固定的。区别在于,窗口之间并不是首尾相接的,而是可以“错开”一定的位置。如果看作一个窗口的运动,那么就像是向前小步“滑动”一样。定义滑动窗口的参数有两个:除去窗口大小(window size)之外,还有一个滑动步长(window slide),代表窗口计算的频率。

Sliding Windows

3. 会话窗口(Session Windows)

借用会话超时失效的机制来描述窗口简单来说,就是数据来了之后就开启一个会话窗口,如果接下来还有数据陆续到来,那么就一直保持会话;如果一段时间一直没收到数据,那就认为会话超时失效,窗口自动关闭。与滑动窗口和滚动窗口不同,会话窗口只能基于时间来定义,而没有“会话计数窗口”的概念。

考虑到事件时间语义下的乱序流,在 Flink 底层,对会话窗口的处理会比较特殊:每来一个新的数据,都会创建一个新的会话窗口;然后判断已有窗口之间的距离,如果小于给定的 size,就对它们进行合并(merge)操作。在 Window 算子中,对会话窗口会有单独的处理逻辑。

Session Windows

4. 全局窗口(Global Windows)

这种窗口全局有效,会把相同 key 的所有数据都分配到同一个窗口中;说直白一点,就跟没分窗口一样。无界流的数据永无止尽,所以这种窗口也没有结束的时候,默认是不会做触发计算的。如果希望它能对数据进行计算处理,还需要自定义触发器(Trigger)

Global Windows

二、Keyed Streams和Non-Keyed Streams

Flink 窗口在 keyed streams 和 non-keyed streams 上使用的基本结构。 我们可以看到,这两者唯一的区别仅在于:keyed streams 要调用 keyBy(…)后再调用 window(…) , 而 non-keyed streams 只用直接调用 windowAll(…)。

在Apache Flink中,数据流可以被分为两种类型:Keyed Streams和Non-Keyed Streams。

Keyed Streams是指在处理数据流时需要对数据进行分区,并将数据按照某个Key进行分组的数据流。Keyed Streams中的每个元素都有一个Key,通过这个Key来确定数据所属的分区。在Flink中,Keyed Streams通常用于聚合操作,如GroupBy和Window。

相反,Non-Keyed Streams是指在处理数据流时不需要按照某个Key进行分组的数据流。在Non-Keyed Streams中,每个元素都是相互独立的,没有固定的分区和分组关系。Non-Keyed Streams通常用于一些基于时间的操作,如TimeWindow和ProcessFunction。

区分Keyed Streams和Non-Keyed Streams的方法是,当定义数据流时,如果调用了DataStream的keyBy()方法,那么这个数据流就是Keyed Streams。如果没有调用keyBy()方法,则这个数据流就是Non-Keyed Streams。

    import org.apache.flink.streaming.api.scala._

    val env = StreamExecutionEnvironment.getExecutionEnvironment

    val stream: DataStream[(String, Int)] = env
      .socketTextStream("localhost", 9999)
      .map { value =>
        val Array(key, count) = value.split(",")
        (key, count.toInt)
      }
      .keyBy(_._1) // 调用keyBy方法,将数据按照Tuple2的第一个元素进行分组,因此这个数据流是Keyed Streams

注意的是,在Scala中,对于Tuple类型的数据,可以直接使用元组的语法来访问元素。例如,可以使用_1来访问Tuple的第一个元素,使用2来访问第二个元素。因此,在上面的示例中,keyBy(._1)可以理解为按照Tuple的第一个元素进行分组。

    import org.apache.flink.streaming.api.scala._

    val env = StreamExecutionEnvironment.getExecutionEnvironment

    val stream: DataStream[String] = env
      .socketTextStream("localhost", 9999)
      .flatMap { value =>
        value.split(" ")
      } // 没有调用keyBy方法,因此这个数据流是Non-Keyed Streams