滴滴技术 | Presto 在滴滴的探索与实践

桔妹导读: Presto 在滴滴内部发展三年,已经成为滴滴内部 Ad-Hoc 和 Hive SQL 加速的首选引擎。目前服务 6K+ 用户,每天读取 2PB ~ 3PB HDFS 数据,处理 30 万亿~35 万亿条记录,为了承接业务及丰富使用场景,滴滴 Presto 需要解决稳定性、易用性、性能、成本等诸多问题。我们在 3 年多的时间里,做了大量优化和二次开发,积攒了非常丰富的经验。本文分享了滴滴对 Presto 引擎的改进和优化,同时也提供了大量稳定性建设经验。

1. Presto 简介

▍1.1 简介

Presto 是 Facebook 开源的 MPP(Massive Parallel Processing)SQL 引擎,其理念来源于一个叫 Volcano 的并行数据库,该数据库提出了一个并行执行 SQL 的模型,它被设计为用来专门进行高速、实时的数据分析。Presto 是一个 SQL 计算引擎,分离计算层和存储层,其不存储数据,通过 Connector SPI 实现对各种数据源(Storage)的访问。

▍1.2 架构

Presto 沿用了通用的 Master-Slave 架构,一个 Coordinator,多个 Worker。Coordinator 负责解析 SQL 语句,生成执行计划,分发执行任务给 Worker 节点执行;Worker 节点负责实际执行查询任务。Presto 提供了一套 Connector 接口,用于读取元信息和原始数据,Presto 内置有多种数据源,如 Hive、MySQL、Kudu、Kafka 等。同时,Presto 的扩展机制允许自定义 Connector,从而实现对定制数据源的查询。假如配置了 Hive Connector,需要配置一个 Hive MetaStore 服务为 Presto 提供 Hive 元信息,Worker 节点通过 Hive Connector 与 HDFS 交互,读取原始数据。

▍1.3 实现低延时原理

Presto 是一个交互式查询引擎,我们最关心的是 Presto 实现低延时查询的原理,以下几点是其性能脱颖而出的主要原因:

  • 完全基于内存的并行计算
  • 流水线
  • 本地化计算
  • 动态编译执行计划
  • 小心使用内存和数据结构
  • GC 控制
  • 无容错

2. Presto 在滴滴的应用

▍2.1 业务场景

  • Hive SQL 查询加速
  • 数据平台 Ad-Hoc 查询
  • 报表(BI 报表、自定义报表)
  • 活动营销
  • 数据质量检测
  • 资产管理
  • 固定数据产品

▍2.2 业务规模

▍2.3 业务增长

▍2.4 集群部署

目前 Presto 分为混合集群和高性能集群,如上图所示,混合集群共用 HDFS 集群,与离线 Hadoop 大集群混合部署,为了防止集群内大查询影响小查询, 而单独搭建集群会导致集群太多,维护成本太高,我们通过指定 Label 来做到物理集群隔离(详细后文会讲到)。而高性能集群,HDFS 是单独部署的,且可以访问 Druid, 使 Presto 具备查询实时数据和离线数据能力。

2.5 接入方式

二次开发了 JDBC、Go、Python、Cli、R、NodeJs 、HTTP 等多种接入方式,打通了公司内部权限体系,让业务方方便快捷的接入 Presto 的,满足了业务方多种技术栈的接入需求。

Presto 接入了查询路由 Gateway,Gateway 会智能选择合适的引擎,用户查询优先请求 Presto,如果查询失败,会使用 Spark 查询,如果依然失败,最后会请求 Hive。在 Gateway 层,我们做了一些优化来区分大查询、中查询及小查询,对于查询时间小于 3 分钟的,我们即认为适合 Presto 查询,比如通过 HBO(基于历史的统计信息)及 JOIN 数量来区分查询大小,架构图见:

3. 引擎迭代

我们从 2017 年 09 月份开始调研 Presto,经历过 0.192、0.215,共发布 56 次版本。而在 19 年初(0.215 版本是社区分家版本),Presto 社区分家,分为两个项目,叫 PrestoDB 和 PrestoSQL,两者都成立了自己的基金会。我们决定升级到 PrestoSQL 最新版本(340 版本)原因是:

  • PrestoSQL 社区活跃度更高,PR 和用户问题能够及时回复
  • PrestoDB 主要主力还是 Facebook 维护,以其内部需求为主
  • PrestoDB 未来方向主要是 ETL 相关的,我们有 Spark 兜底,ETL 功能依赖 Spark、Hive

4. 引擎改进

在滴滴内部,Presto 主要用于 Ad-Hoc 查询及 Hive SQL 查询加速,为了方便用户能尽快将 SQL 迁移到 Presto 引擎上,且提高 Presto 引擎查询性能,我们对 Presto 做了大量二次开发。同时,因为使用 Gateway,即使 SQL 查询出错,SQL 也会转发到 Spark 及 Hive 上,所以我们没有使用 Presto 的 Spill to Disk 功能。这样一个纯内存 SQL 引擎在使用过程中会遇到很多稳定问题,我们在解决这些问题时,也积累了很多经验,下面将一一介绍:

▍4.1 Hive SQL 兼容

18 年上半年,Presto 刚起步,滴滴内部很多用户不愿意迁移业务,主要是因为 Presto 是 ANSI SQL,与 HiveQL 差距较大,且查询结果也会出现结果不一致问题,迁移成本比较高,为了方便 Hive 用户能顺利迁移业务,我们对 Presto 做了 Hive SQL 兼容。而在技术选型时,我们没有在 Presto 上层,即没有在 Gateway 这层做 SQL 兼容,主要是因为开发量较大,且 UDF 相关的开发和转换成本太高,另外就是需要多做一次 SQL 解析,查询性能会受到影响,同时增加了 Hive Metastore 的请求次数,当时 Hive Metastore 的压力比较大,考虑到成本和稳定性,我们最后选择在 Presto 引擎层上兼容。

主要工作:

  • 隐式类型转换
  • 语义兼容
  • 语法兼容
  • 支持 Hive 视图
  • Parquet HDFS 文件读取支持
  • 大量 UDF 支持
  • 其他

Hive SQL 兼容,我们迭代了三个大版本,目前线上 SQL 通过率 97~99%。而业务从 Spark/Hive 迁移到 Presto 后,查询性能平均提升 30%~50%,甚至一些场景提升 10 倍,Ad-Hoc 场景共节省 80% 机器资源。下图是线上 Presto 集群的 SQL 查询通过率及失败原因占比,'null' 表示查询成功的 SQL,其他表示错误原因:

▍4.2 物理资源隔离

上文说到,对性能要求高的业务与大查询业务方混合跑,查询性能容易受到影响,只有单独搭建集群。而单独搭建集群导致 Presto 集群太多,维护成本太高。因为目前我们 Presto Coordinator 还没有遇到瓶颈,大查询主要影响 Worker 性能,比如一条大 SQL 导致 Worker CPU 打满,导致其他业务方 SQL 查询变慢。所以我们修改调度模块,让 Presto 支持可以动态打 Label,动态调度指定的 Label 机器。如下图所示:

根据不同的业务划分不同的 label,通过配置文件配置业务方指定的 label 和其对应的机器列表,Coordinator 会加载配置,在内存里维护集群 label 信息,同时如果配置文件里 label 信息变动,Coordinator 会定时更新 label 信息,这样调度时根据 SQL 指定的 label 信息来获取对应的 Worker 机器,如指定 label A 时,那调度机器里只选择 Worker A 和 Worker B 即可。这样就可以做到让机器物理隔离了,对性能要求高的业务查询既有保障了。

▍4.3 Druid Connector

使用 Presto + HDFS 有一些痛点:

  • latency 高,QPS 较低
  • 不能查实时数据,如果有实时数据需求,需要再构建一条实时数据链路,增加了系统的复杂性
  • 要想获得极限性能,必须与 HDFS DataNode 混部,且 DataNode 使用高级硬件,有自建 HDFS 的需求,增加了运维的负担

所以我们在 0.215 版本实现了 Presto on Druid Connector,此插件有如下优点:

  • 结合 Druid 的预聚合、计算能力(过滤聚合)、Cache 能力,提升 Presto 性能(RT 与 QPS)
  • 让 Presto 具备查询 Druid 实时数据能力
  • 为 Druid 提供全面的 SQL 能力支持,扩展 Druid 数据的应用场景
  • 通过 Druid Broker 获取 Druid 元数据信息
  • 从 Druid Historical 直接获取数据
  • 实现了 Limit 下推、Filter 下推、Project 下推及 Agg 下推

在 PrestoSQL 340 版本,社区也实现了 Presto on Druid Connector,但是此 Connector 是通过 JDBC 实现的,缺点比较明显:

  • 无法划分多个 Split,查询性能差
  • 请求查询 Broker,之后再查询 Historical,多一次网络通信
  • 对于一些场景,如大量 Scan 场景,会导致 Broker OOM
  • Project 及 Agg 下推支持不完善

详细架构图见:

使用了 Presto on Druid 后,一些场景,性能提升 4~5 倍。



▍4.4 易用性建设

为了支持公司的几个核心数据平台,包括:数梦、提取工具、数易及特征加速及各种散户,我们对 Presto 做了很多二次开发,包括权限管理、语法支持等,保证了业务的快速接入。主要工作:

  • 租户与权限
    • 与内部 Hadoop 打通,使用 HDFS SIMPLE 协议做认证
    • 使用 Ranger 做鉴权,解析 SQL 使 Presto 拥有将列信息传递给下游的能力,提供用户名 + 数据库名/表名/列名,四元组的鉴权能力,同时提供多表同时鉴权的能力
    • 用户指定用户名做鉴权和认证,大账号用于读写 HDFS 数据
    • 支持视图、表别名鉴权
  • 语法拓展
    • 支持 add partition
    • 支持数字开头的表
    • 支持数字开头的字段
  • 特性增强
    • insert 数据时,将插入数据的总行数写入 HMS,为业务方提供毫秒级的元数据感知能力
    • 支持查询进度滚动更新,提升了用户体验
    • 支持查询可以指定优先级,为用户不同等级的业务提供了优先级控制的能力
    • 修改通信协议,支持业务方可以传达自定义信息,满足了用户的日志审计需要等
    • 支持 DeprecatedLzoTextInputFormat 格式
    • 支持读 HDFS Parquet 文件路径

▍4.5 稳定性建设

Presto 在使用过程中会遇到很多稳定性问题,比如 Coordinator OOM,Worker Full GC 等,为了解决和方便定位这些问题,首先我们做了监控体系建设,主要包括:

  • 通过 Presto Plugin 实现日志审计功能
  • 通过 JMX 获取引擎指标将监控信息写入 Ganglia
  • 将日志审计采集到 HDFS 和 ES;统一接入运维监控体系,将所有指标发到 Kafka;
  • Presto UI 改进:可以查看 Worker 信息,可以查看 Worker 死活信息

通过以上功能,在每次出现稳定性问题时,方便我们及时定位问题,包括指标查看及 SQL 回放等,如下图所示,可以查看某集群的成功及失败 SQL 数,我们可以通过定义查询失败率来触发报警:

在 Presto 交流社区,Presto 的稳定性问题困扰了很多 Presto 使用者,包括 Coordinator 和 Worker 挂掉,集群运行一段时间后查询性能变慢等。我们在解决这些问题时积累了很多经验,这里说下解决思路和方法。

根据职责划分,Presto 分为 Coordinator 和 Worker 模块,Coordinator 主要负责 SQL 解析、生成查询计划、Split 调度及查询状态管理等,所以当 Coordinator 遇到 OOM 或者 Coredump 时,获取元信息及生成 Splits 是重点怀疑的地方。而内存问题,推荐使用 MAT 分析具体原因。如下图是通过 MAT 分析,得出开启了 FileSystem Cache,内存泄漏导致 OOM。

这里我们总结了 Coordinator 常见的问题和解决方法:

  • 使用 HDFS FileSystem Cache 导致内存泄漏,解决方法禁止 FileSystem Cache,后续 Presto 自己维护了 FileSystem Cache
  • Jetty 导致堆外内存泄漏,原因是 Gzip 导致了堆外内存泄漏,升级 Jetty 版本解决
  • Splits 太多,无可用端口,TIME_WAIT 太高,修改 TCP 参数解决
  • JVM Coredump,显示"unable to create new native thread",通过修改 pid_max 及 max_map_count 解决
  • Presto 内核 Bug,查询失败的 SQL 太多,导致 Coordinator 内存泄漏,社区已修复

而 Presto Worker 主要用于计算,性能瓶颈点主要是内存和 CPU。内存方面通过三种方法来保障和查找问题:

  • 通过 Resource Group 控制业务并发,防止严重超卖
  • 通过 JVM 调优,解决一些常见内存问题,如 Young GC Exhausted
  • 善用 MAT 工具,发现内存瓶颈

而 Presto Worker 常会遇到查询变慢问题,两方面原因,一是确定是否开启了 Swap 内存,当 Free 内存不足时,使用 Swap 会严重影响查询性能。第二是 CPU 问题,解决此类问题,要善用 Perf 工具,多做 Perf 来分析 CPU 为什么不在干活,看 CPU 主要在做什么,是 GC 问题还是 JVM Bug。如下图所示,为线上 Presto 集群触发了 JVM Bug,导致运行一段时间后查询变慢,重启后恢复,Perf 后找到原因,分析 JVM 代码,可通过 JVM 调优或升级 JVM 版本解决:

这里我们也总结了 Worker 常见的问题和解决方法:

  • Sys load 过高,导致业务查询性能影响很大,研究 jvm 原理,通过参数(-XX:PerMethodRecompilationCutoff=10000 及 -XX:PerBytecodeRecompilationCutoff=10000)解决,也可升级最新 JVM 解决
  • Worker 查询 hang 住问题,原因 HDFS 客户端存在 bug,当 Presto 与 HDFS 混部署,数据和客户端在同一台机器上时,短路读时一直 wait 锁,导致查询 Hang 住超时,Hadoop 社区已解决
  • 超卖导致 Worker Young GC Exhausted,优化 GC 参数,如设置-XX:G1ReservePercent=25 及 -XX:InitiatingHeapOccupancyPercent=15
  • ORC 太大,导致 Presto 读取 ORC Stripe Statistics 出现 OOM,解决方法是限制 Protobuf 报文大小,同时协助业务方合理数据治理
  • 修改 Presto 内存管理逻辑,优化 Kill 策略,保障当内存不够时,Presto Worker 不会 OOM,只需要将大查询 Kill 掉,后续熔断机制会改为基于 JVM,类似 ES 的熔断器,比如 95% JVM 内存时,Kill 掉最大 SQL

▍4.6 引擎优化及调研

作为一个 Ad-Hoc 引擎,Presto 查询性能越快,用户体验越好,为了提高 Presto 的查询性能,在 Presto on Hive 场景,我们做了很多引擎优化工作,主要工作:

  • 某业务集群进行了 JVM 调优,将 Ref Proc 由单线程改为并行执行,普通查询由 30S~1 分钟降低为 3-4S,性能提升 10 倍 +
  • ORC 数据优化,将指定 string 字段添加了布隆过滤器,查询性能提升 20-30%,针对一些业务做了调优
  • 数据治理和小文件合并,某业务方查询性能由 20S 降低为 10S,性能提升一倍,且查询性能稳定
  • ORC 格式性能优化,查询耗时减少 5%
  • 分区裁剪优化,解决指定分区但获取所有分区元信息问题,减少了 HMS 的压力
  • 下推优化,实现了 Limit、Filter、Project、Agg 下推到存储层

18 年我们为了提高 Presto 查询性能,也调研了一些技术方案,包括 Presto on Alluxio 和 Presto on Carbondata,但是这 2 种方案最后都被舍弃了,原因是:

  • Presto on Alluxio 查询性能提升 35%,但是内存占用和性能提升不成正比,所以我们放弃了 Presto on Alluxio,后续可能会对一些性能要求敏感的业务使用
  • Presto on Carbondata 是在 18 年 8 月份测试的,当时的版本,Carbondata 稳定性较差,性能没有明显优势,一些场景 ORC 更快,所以我们没有再继续跟踪调研 Presto on Carbondata。因为滴滴有专门维护 Druid 的团队,所以我们对接了 Presto on Druid,一些场景性能提升 4~5 倍,后续我们会更多关注 Presto on Clickhouse 及 Presto on Elasticsearch

5. 总结

通过以上工作,滴滴 Presto 逐渐接入公司各大数据平台,并成为了公司首选 Ad-Hoc 查询引擎及 Hive SQL 加速引擎,下图可以看到某产品接入后的性能提升:

上图可以看到大约 2018 年 10 月该平台开始接入 Presto,查询耗时 TP50 性能提升了 10+ 倍,由 400S 降低到 31S。且在任务数逐渐增长的情况下,查询耗时保证稳定不变。

而高性能集群,我们做了很多稳定性和性能优化工作,保证了平均查询时间小于 2S。如下图所示:

6. 展望

Presto 主要应用场景是 Ad-Hoc 查询,所以其高峰期主要在白天,如下图所示,是网约车业务下午 12-16 点的查询,可以看到平均 CPU 使用率在 40% 以上。

但是如果看最近一个月的 CPU 使用率会发现,平均 CPU 使用率比较低,且波峰在白天 10~18 点,晚上基本上没有查询,CPU 使用率不到 5%。如下图所示:

所以,解决晚上资源浪费问题是我们今后需要解决的难题。

同时,为了不与开源社区脱节,我们打算升级 PrestoDB 0.215 到 PrestoSQL 340 版本,届时会把我们的 Presto on Druid 代码开源出来,回馈社区。

本文作者

滴滴 Presto 引擎负责人,负责带领引擎团队深入 Presto 内核,解决在海量数据规模下 Presto 遇到的稳定性、性能、成本方面的问题。搜索引擎及 OLAP 引擎爱好者,公众号:FFCompute

关于团队

滴滴大数据架构部 OLAP & 检索平台组负责以 Elasticsearch、Clickhouse、Presto 及 Druid 为代表的 OLAP 引擎的内核级极致优化,为滴滴各个产品线提供稳定可靠的 PB 级海量数据的实时数据分析、日志检索、监控及即席查询服务。

博闻强识,招贤纳士,滴滴用广阔的舞台,在这里,等待你!


本文地址:https://www.6aiq.com/article/1602421861788
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出