欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

知乎上Presto缓存优化的实战经验分享

最编程 2024-07-23 07:48:26
...


作者 | 陈曦  知乎数据架构工程师 

本文主要讲解PrestoDB与Trino的测试与选型,使用Alluxio为PrestoDB实现查询加速,以及在知乎中Presto的部署调整与缓存加速实践。


一、背景

Presto作为一个典型的存储与计算分离的OLAP引擎已经在大数据领域取得了一系列成果。存算分离的部署方式虽然在资源扩展性上拥有明显优势,但也不免带来了一系列技术挑战。其中,数据获取的速度与成本问题尤为突出


存储与计算分离的挑战

在这样的架构中,Presto虽然提供了丰富且灵活的Connector,让查询异构数据源变得更为简单。但由于自身缺乏专门的存储系统,低延迟的数据查询往往难以保障。此外,因为必须频繁地与外部存储通信,这使得网络和存储的不稳定性变得更为显著,从而可能影响到数据访问的稳定性。


历程

知乎的Presto经历了PrestoDB -> PrestoSQL -> Trino几个阶段,现在需要重新审视和调整目前的Presto架构,来解决知乎日益增长的复杂分析需求,主要提升报表场景的查询性能,同样也包括但不限于即席查询、探索分析等场景。


二、架构

2.1 架构定位

Presto是一种开源的分布式OLAP SQL查询引擎,它最初由Facebook开发,并于2012年开源。因为丰富的连接器特性 (Connector) 它不仅可以支持非关系数据源,例如 Hadoop 分布式文件系统 (HDFS,Hive)、S3、Cassandra,还可以支持关系型数据源,例如 MySQL、PostgreSQL、Oracle 等。对于这些异构数据域均支持标准 ANSI SQL 语义,同时还支持复杂查询、聚合、联接、左 / 右外联接、子查询、开窗函数等。


Presto的架构与经典大规模并行处理 (MPP) 数据库管理系统相似,由一个协调器节点 (Coordinator) 与多个工作节点 (Worker) 组成。协调器节点负责接收来自客户端的查询请求并进行解析、优化、调度。然后将任务分配到多个工作节点中,所有处理都在内存中进行,并以流水线方式 (Pipeline) 在集群中流动,从而避免不必要的 I/O 开销。


在大数据OLAP领域中,衡量一个引擎的标准主要有数据量、灵活性、性能,并且只能侧重于一点。根据前面的 Presto 架构介绍,将它定位为中小型数据量、高度灵活、性能适中的数据场景,能够发挥出最大的价值。


所以在面向实际业务场景中,Presto特别适合交互式分析、报表查询、数据探索等需要快速失败 (Fail-fast) 的场景。不太适合那种需要任务高容错的场景,比如大规模 ETL、数据同步、数据回溯、机器学习数据建模。同时,因为在知乎使用 Presto 的首要任务是为 Hive 查询提速,所以也不建议承接高并发查询场景。


最终,我们在知乎大数据中把 Presto 定位成了 “只读OLAP引擎”,部署在大数据的计算领域架构下。其他读写场景的需求会推荐使用 Hive 或者 Spark,或者根据业务需要选择其他引擎。


(注: 下图只有计算领域 ,省略了其他环节。)


2.2 部署方式

Presto 在知乎的部署经历了两种方式的迭代:

资源物理隔离方式;

资源统一网关路由方式。

2.2.1 资源物理隔离方式

在早期的PrestoDB与PrestoSQL时代,由于缺乏高效基础设施运维的便利性以及网关路由对资源的负载均衡机制,系统主要采用物理隔离的方式进行部署。


这种方式虽然满足了业务使用的便捷性,但也导致了一系列问题,如维护复杂性增加、资源分布不均、变更成本增高及资源浪费等。


2.2.2 资源统一网关路由方式

为了优化资源管理和简化用户访问,我们在集群入口引入了presto-gateway。


借助此项目,我们实现了将原先 N 个集群的独立域名整合为一个统一域名,这既为后续的 AB 测试、灰度升级和特定用户路由提供了便利,同时也显著降低了资源的浪费。


在基础运维策略上,我们整合了Terraform,将传统手动运维工作转化成更为逻辑化的软件开发流程。


利用Terraform的幂等状态管理设计,所有的运维操作都可以经过代码审查后使用单一的命令来完成。虽然这种转变略微增加了初期的开发成本,但在长期看来,其为我们节省了大量的日常维护成本,降低了 99% 的维护心智复杂度。


需要强调的是,这里提到的 “单一命令” 并不仅仅是某种简化的一键操作,而是遵循基础设施即代码Infrastructure as Code (IaC) 开发后得到的,意味着所有的部署和后续变更都可以通过这个命令来高效完成。



三、目标

在不增加机器成本的前提下:

查询耗时需要呈下降的良性趋势。

报表类的中小型查询场景 (P50) 相较于之前,至少有50%的性能提升。


四、缓存模式选择

我们的目标,有一个硬性前提,不能增加机器成本,根据这个硬性前提,有如下两种部署模式:

独立Alluxio集群,可以实现OLAP引擎统一加速,不局限于Presto还可以为 Hive,Spark进行加速,最终提供统一的数据编排服务。

RaptorX,此为PrestoDB中的一个专属缓存加速功能,打开配置后即可使用。


4.1 独立 Alluxio 集群模式

Allxuio是什么?

Alluxio是一个开源的分布式超大规模数据编排系统,旨在加速分布式计算工作负载的数据访问速度。它可以在内存和存储之间提供一种高效的数据访问层,以便在不同的数据存储系统(如 HDFS、S3、Azure Blob 等)和计算框架(如 Presto、Apache Spark 等)之间进行快速数据共享。


4.2 RaptorX

PrestoDB RaptorX:缓存加速

RaptorX前身是Raptor,它是一个Presto Connector (presto-raptor),主要用于支持 Meta(以前的 Facebook)中一些关键的交互式查询场景。PrestoDB存算分离的架构,这种方式非常灵活,但很难提供低延迟保证,尤其是网络和存储延迟等问题会被放大。为了解决这些问题,Raptor被设计成PrestoDB的独享存储引擎 (shared-nothing storage engine)。

注意,这里提到的RaptorX与Raptor是两个项目。Raptor 可以按照是一个独立的缓存集群理解,RaptorX 则是多级缓存功能,在比如元数据、文件列表、原始数据等功能使用时,赋予自动缓存的能力。

通过实现自有存储,Presto也在往低延迟、高吞吐的OLAP引擎道路上迈出脚步。同时这种架构变更也带来了如下问题:

资源利用率不可控:Raptor集群的大小取决于需要存储多少数据,但是因为架构变更数据与计算耦合在一起了,这对资源使用率带来了新的挑战。

高工程和运营成本:虽然Raptor实现了自有存储,这对数仓工作等与数据相关的场景带来了好处,但是对于Presto来说需要多维护一套存储系统。

潜在的安全问题:随着安全性和隐私要求的增加,实现统一的安全性和隐私政策变得更加重要。使用单独的存储引擎使得执行此类政策变得极为困难和脆弱。

因此从 2019 年开始,Meta的工程师们就在重新思考Raptor的未来,是否有可能既从本地存储中受益,又无需承担存算紧耦合架构带来的成本提升?最终确定的方向是在原生数据仓库之上添加一个新的本地缓存层。项目命名为 RaptorX (详情 https://prestodb.io/blog/2021/02/04/raptorx)。


从技术层面来讲,RaptorX项目与Raptor无关。主要的区别是利用本地存储与单独的存储系统,带来的好处如下:

Presto无需管理数据生命周期;

单个Worker故障导致数据丢失对查询性能的影响较小;

缓存作为文件系统层的一个特性,是presto-hive连接器的一部分,运维方式与常规Presto集群一样,从而减少了运维成本。

PrestoDB RaptorX:功能介绍

原始数据缓存: 可以将查询需要的原始数据缓存下来,缓存模式有 2 种类型 FILE_MERGE 与 ALLUXIO;

  • FILE_MERGE 是使用本地存储。

  • ALLUXIO 使用嵌入式的 ALLUXIO,一般称为 Alluxio Local Cache。

元数据缓存: 在 Coordinator 中缓存 Hive 表 / 分区等信息;

文件列表缓存: 缓存来自远程存储分区目录的文件列表;

分片结果缓存: 在 Worker 节点的本地存储上缓存部分计算结果,比如 COUNT DISTINCT;

Footer 缓存: 在 Worker 节点内存中缓存 ORC 和 Parquet 的 Footer 信息,这些数据大多在读取时会频繁使用;

亲和性调度器: 将查询与缓存赋予亲和性,根据哈希取模或者一致性哈希的方式将查询调度到缓存数据所处的节点,以此提升缓存命中率。


4.3 选择结论

尽管独立部署的 Alluxio 集群拥有多项优势,然而考虑到我们当前使用的 Presto 在 Kubernetes(K8s)环境中运行,将独立 Alluxio 集群集成进现有环境中可能面临网络、磁盘和副本等方面的高成本运维挑战。鉴于初期的考量,为了避免过多投入资源在这些方面,我们选择了目前具有性价比的方案,使用 PrestoDB RaptorX。


独立维护一个完整的集群与简单地进行配置调整相比,短期内无疑后者更具性价比。然而,从长远来看,选择独立部署的 Alluxio 集群将在大数据的 OLAP 领域带来不可忽视的优势


五、OLAP 性能测试

5.1 现状

从先前对 RaptorX 项目的探讨及社区中相关的实践案例分析可知,利用 Alluxio (独立式或嵌入式) 可以优化和挖掘服务器中的闲置资源,从而提高查询速度是可行的。


知乎在调整 Presto 的集群部署策略之后,发现了若干未充分利用的资源。这些资源为我们提供了一个绝佳的机会,去解决一些早期服务的遗留问题。


得益于集群部署策略的优化,现在我们已经拥有了快速部署和在 PrestoDB 与 Trino 之间切换的能力,并且还能进行异构部署。


基于这样的背景,我们决定再次进行深入的对比测试,重新评估 PrestoDB 和 Trino 在 OLAP 性能上的表现,以便为我们的用户提供更优质的数据查询服务。


5.2 TPC-DS 测试

5.2.1 版本

测试版本为 PrestoDB 0.280 vs Trino 416 (它们为 2023-05 月期间的最新版本)。


5.2.2 集群规模

96C 192G 的云主机;

500GB TPC-DS 数据集,造数项目 spark-sql-perf 和 tpcds-kit;

数据模拟生产开启了 snappy 压缩,数据格式有 Parquet 和 ORC;

Trino 使用了 jdk17,PrestoDB 使用了 jdk11;

1Coordinator 3Worker 和 6Worker 两种部署方式 (独立部署),Worker 的堆内存为 64GB,其他参数尽量与生产一致;

测试使用了原生 PrestoDB 或 Trino 的 Java 客户端;

测试用例由源码中的 tpcds 所提供。


5.2.3 TPC-DS 测试结果

TPC-DS 数据集抽取了 20 个 SQL,对齐配置后,进行了多轮测试。



5.3 业务测试

5.3.1 生产业务测试规模

生产环境中,我们因为有了网关和多集群部署的便利性,摘了一个子集群的资源部署了 PrestoDB ,在闲时进行了对比测试。

k8s 容器化部署;

1Coordinator 64Worker,Worker 堆内存 64GB;

其他参数与 Trino 对齐。

5.3.2 生产 AB 测试结果

测试用例随机从业务中抽取了 20 个 SQL,它们分别代表了不同的业务场景,有报表类、分析类、挖掘类等,几乎能反应真实业务场景。



5.4 PrestoDB 灰度

还是同样的方式,生产环境开始了多轮 AB 测试,采取了 20%、40%、50% 的资源进行 AB 测试。

测试发现,在 P50 场景中平均有 1.5 ~ 2 倍的性能提升,在 P90 场景中只有少量的提升。



5.5 PrestoDB 生产优化

5.5.1 TPC-DS 的结果为什么是 Trino 更优

Trino 的社区发展接受了很多用户的意见,很多易用性等细节功能开发走在了前面,这次测试我们发现如下几点相对于 PrestoDB 更优。


5.5.1.1 动态过滤

Trino 会对 TPC-DS 中的一些场景,实现动态过滤的优化,如果命中了该场景优化效果特别明显。


虽然 PrestoDB 也有这功能,可以理解 Trino 加大了开发投入所以走在了前面。但是这种场景特别考验数仓的设计,在我们对实际业务测试中,很难命中这个场景。


详情见:https://trino.io/docs/current/admin/dynamic-filtering.html


5.5.1.2 享受 Spark 统计信息加成

Trino 可以享受 Spark ETL 产出时的统计信息加成。


这个统计信息是在数据产出后直接被 Spark ETL 写到了 Hive 表的 DDL 信息里面,如下图:



因为 Trino 可以享受该加成,所以会比 PrestoDB 要快,我们测试将这些统计信息去掉后,查询耗时回到了持平状态。

详情见:

https://github.com/trinodb/trino/pull/16120


5.5.1.3 参考 Trino 的优化参数

经过对比发现 Trino 的很多默认参数都比 PrestoDB 投入的开发更多,比如在 PrestoDB 还是实验性的参数,在 Trino 中已经迭代了几轮并正式上线了。所以我们在测试的时候,PrestoDB 的很多参数都是参考 Trino 在进行调优。

主要参数如下:

下推类hive.pushdown_filter_enabled=false,hive.parquet_pushdown_filter_enabled=true

下推类hive.enable-parquet-dereference-pushdown=true

下推类hive.parquet.pushdown-filter-enabled=true

上一篇: 如何在Hive中使用PARTITION BY子句的窗口函数详解

下一篇: 详解ROW_NUMBER()函数的使用方法:分组与排序示例大公开

推荐阅读