作者 | Sergio De Simone

译者 | 平川

策划 | Tina

Uber 使用开源分布式 SQL 查询引擎 Presto 来提供跨多个数据源的分析,包括 Apache Hive、Apache Pinot、MySQL 和 Apache Kafka。为了提高性能,Uber 工程师 探索 了多种处理快速查询的方式,发现它们可以提高 Presto 的利用率和响应速度。

快速查询,即在两分钟内执行的查询,约占 Uber 分析过程中总查询数的一半。Uber 工程师发现,处理这些查询与处理其他查询一样,会造成利用率不足和高延迟,因为为了避免系统超载,要对它们进行限流。

为了防止快速查询被限流,第一步是预测传入的查询在什么情况下是快速查询。Uber 工程师根据历史数据进行预测。在这些数据中,他们给每个查询分配了一个精确的指纹,即在去除注释、空格和任何文字值后计算出的唯一哈希值。

我们使用查询的精确指纹和抽象指纹测试了 P90 和 P95 查询执行时间,回溯窗口分别为 2 天、5 天和 7 天。[......] 我们使用这一候选定义来预测查询是否是快速查询:基于 Z 指纹的查询在过去 Y 天中的运行时间 X 是否少于 2 分钟。利用这一定义,Uber 工程师发现,在预测哪些查询将在两分钟内完成方面,当回溯窗口为 5 天时,抽象指纹的 P90 值提供了最佳准确性和覆盖率。他们还在表中保留了足够多的数据,用于查询传入的查询是否为快速查询,以便能够改用不同的百分位数和 / 或更大或更小的回溯窗口。

虽然这个想法看起来简单明了,但在设计和实现时却经历了几次反复,从让系统管道尽可能晚地处理快速查询到尽可能早地处理它们。

在最初的设计中,Uber 工程师决定根据用户优先级将快速查询和非快速查询排在同一个队列中。也就是说,所有查询,无论是快速查询还是非快速查询,都会进入与其用户优先级相对应的队列中,例如批处理队列或交互式队列。每个队列都有自己指定的 Presto 集群,其中一个子集专门用于在完成所有用户 / 源和集群限制检查后处理快速查询。

这种设计导致快速 Presto 集群的利用率不足,主要原因是将慢速查询和快速查询混放在了同一个队列中,慢速查询影响了快速查询到达快速集群的速度。

在第二次设计尝试中,Uber 工程师尝试为快速查询设置了专用队列,它们进入队列后一旦验证步骤完成就会被路由。因此,快速查询可以更快地到达指定的快速集群,从而使超过 75% 的预定查询其端到端 SLA 有了数量级的提升。

虽然这种方法提高了集群利用率,缩短了快速查询的运行时间,但 Uber 工程师仍在考虑如何进一步改进这种方法。事实上,在生产中运行这一设计时,他们发现,快速查询处理的速度非常快,以至于没有必要根据优先级在不同队列中路由快速查询。

这使得 Uber 工程师开始着手进一步改进设计,为快速查询实现一个完全专用的子系统,这有望进一步提高集群利用率并简化路由逻辑。

https://www.infoq.com/news/2024/11/uber-presto-express/