Apache Flink 与 RisingWave:流处理性能报告公开预览版

744 阅读13分钟

作者:RisingWave 创始人 & CEO 吴英骏

无论时代如何更迭,性能总是一个广受关注的话题。

Apache Flink 是一个在大数据时代广受欢迎的分布式流处理框架;RisingWave 是一个云时代诞生的流处理数据库。作为一个使用 Rust 开发、完全面向云环境设计的流处理系统,RisingWave 宣称已经取得了相比于 Flink 十倍的性能提升

为了得到极致性能,RisingWave 已经进行了长达半年的性能测试。在上周,RisingWave 团队向 50 余位社区伙伴分享了私有预览版性能报告。这些社区伙伴中超过一半对 Flink 拥有 3 年以上使用经验,并且有不少也是 Flink 项目的代码贡献者与 PMC 。我们从这些伙伴中学习到了新的 Flink 调优经验,同时也收获了不少肯定与鼓励。

在今天(2023 年 7 月 4 日),我们将公开发布性能报告预览版,供社区伙伴了解 RisingWave 与 Flink 项目的性能对比。

注:由于系统版本不断迭代,本性能报告仅代表2023年7月4日之前测得的数据。我们会定期更新报告,让大家更加全面的理解 Flink 与 RisingWave 的性能。

开始之前

尽管 Apache Flink 与 RisingWave 都是开源流处理系统,使用场景有大量重合,但两者的设计理念有较大差别,直接将两者进行对比其实有失公允。在开始比较之前,我们先概括两个项目的异同,供大家参考。

Flink(flink.apache.org/)作为大数据时代便诞生发展的项目,其设计理念是成为通用流批计算平台。尽管因其高效的流处理而著名,Flink 目前正在向大一统系统进化:其不断向批处理、机器学习(Flink ML)、数据湖(Paimon,或称 Flink Table Store)、有状态函数(StateFun)等方向发展。与此同时,Flink 主要提供类似 MapReduce 方式的 Java API,并在 Java API 的基础上提供了更高层的 Python 与 SQL 接口。

RisingWave(www.risingwave.dev/)作为流处理数据库,其设计理念是大幅提升云上流计算性能效益。RisingWave 与 PostgreSQL 协议兼容,用户可通过 psql、JDBC 等连接PostgreSQL 的方式来连接 RisingWave。RisingWave 不提供类似 MapReduce 的 Java API,但提供 Python UDF 以提升 SQL 语言的表达性能。

为了求同存异,我们主要对比 Flink SQL 与 RisingWave。

基准测试与环境

我们使用 Flink 1.16 版本与 RisingWave 0.19 版本进行对比。

在流处理领域,最常用的基准测试是 Nexmark。

由于 Nexmark 基准测试设计的年代较早,并没有覆盖一些常用的 SQL 算子,因此我们又额外添加了 5 条查询语句。

为模拟真实场景,我们使用 Kafka 不断产生数据,并在下游分别接上 Flink 与 RisingWave 进行对比。我们专注于吞吐量,而非延迟。对于 Flink 与 RisingWave ,我们均要求保证 exactly once 语义。

由于具体测试环境,包括 Flink 的调优,相对复杂,我们并不在此详细列出。感兴趣的朋友可以关注微信公众号:RisingWave 中文开源社区进行深入了解交流。

硬件配置

测试中使用到的机型均为 AWS EC2 c5.2xLarge (8vCPUs 16GB memory)。

Risingwave 一共有四个组件:1. Frontend Node; 2. Meta Node; 3. Compute Node; 4. Compactor Node. Compute Node和Compactor Node 共享同一台机器。Frontend Node 和Meta Node 对性能无影响,他们共享另一台机器。

Flink 则有两个组件:1. Job Manager; 2. Task Manager. 基于对应关系,Task Manager 独占一台机器(RocksDB 中的 compaction threads 对应了 Compactor Node),而 Job Manager 则独占另一台机器。

性能测试结果

我们直接放出测试结果,如下图。

RisingWave 与 Flink 的性能对比。100% 代表 RisingWave 与 Flink 性能相同。大于 100% 代表 RisingWave 更快,小于 100% 代表 Flink 更快。

注意,在本图中我们对比了两个版本的 Flink:Flink 与 Flink (better storage)。这原因是我们使用了 EBS 存储 RocksDB 状态。默认的 EBS gp3 性能参数为 3000 IOPS 与 125 MB/s。由于在流处理中,内部状态管理很可能是性能的瓶颈,我们在使用默认 EBS gp3 的基础上,又将性能参数提升到 12000 IOPS 与 500 MB/s,以此来提升 Flink 性能。

可以看到,在列出的 27 条 query 中,RisingWave 在 22 条 query 中获得了性能优势。其中,在 12 条 query 中都跑出了相比于 Flink 至少高出 50% 的性能(即图中大于 150%),10 条query 超过 Flink 性能两倍(即图中大于 200%)。其中,q102 获得了大于 520 倍的性能提升,q104 获得了 660 倍的性能提升。

详细解释

为什么缺少了部分查询结果?

如果大家仔细观察,可以发现,我们并没有放出 q6、q11、q13、q14 这四条查询语句的结果。原因如下:

  • q6:该查询使用了窗口函数,当前版本的 RisingWave 暂时不支持该查询,但很快就会支持(本月底);
  • q11:该查询使用了 session window,而我们认为该功能并非高优先级,因此目前没有实现;
  • q13:需要生成 side_input,忽略;
  • q14:该查询需要 UDF 支持。尽管 RisingWave 支持 UDF,但这要求部署单独的 UDF 服务,而我们主要是测试 RisingWave 与 Flink 的性能,因此忽略。

为什么无状态计算的性能看起来差不多?

在 q0-q3 以及 q21、q22 中,RisingWave 相比于 Flink 的性能提升并不多。这点是反直觉的:RisingWave 作为一个由 Rust 开发的项目,理论上应该轻易达到由 Java 开发的 Flink 的数倍性能。

但为什么在测试中,并没有观察到这样的性能提升?这主要是因为在这些查询中,Flink/RisingWave 与 Kafka 之间进行的信息传输所引入的网络 IO 成为了主要瓶颈。也就是说,大量时间被消耗在了网络传输上,而非 CPU 计算上。实际上,我们的确可以构造出复杂无状态计算(如对 nested json 进行解析等)来体现出 Rust 项目的性能优势,但 Nexmark 并不涵盖此类操作,为公平起见,我们忽略了该类测试。

如何解释 q5 与 q16 这两条 RisingWave 明显慢于 Flink 的查询?

在 Flink 的测试中,我们保留了 Flink 官方所使用的 Nexmark 源代码对于 source 的定义。与Risingwave 的唯一区别在于:Flink 在 nexmark 数据源上定义了 watermark,即水位线。水位线的存在,截至目前,为 Flink 带来了额外的优化机会。比如一个窗口聚合函数可以在水位线到来时,判定某一个时间窗口可以关闭、并输出一次且仅有一次最终结果,而不是在某一行的更新之后实时输出最新的中间结果。 我们将前者的语义称为: Emit On Window Close (EOWC)。我们注意到,Flink 不支持非 EOWC 语义的窗口聚合函数,于是我们也无法通过去除 watermark 的定义来强制 Flink 使用非 EOWC 语义。在 Risingwave 用户的生产环境使用中,他们向我们反馈对两种语义都有需求。

Risingwave 马上会支持 EOWC,但在上述的测试中还尚未支持。Flink 的 q5 采用了这样的语义,而 Risingwave 则仍旧输出大量的实时中间结果。这个区别导致了 Risingwave 的下游 join算子会被大量触发,导致性能低于 Flink。我们将在下一次的测试中,为大家呈现支持 EOWC后的结果。

对于 q16,在优化执行计划时,Risingwave 目前尚未提供如nightlies.apache.org/flink/flink… 中所描述的 Split Distinct Aggregation 的优化。我们将在支持之后,在下一版的性能报告中,提供相同执行计划的性能数据。

RisingWave 在哪类查询上显著优于 Flink?

对内部状态复杂占用空间大的查询,RisingWave 几乎肯定能够实现性能翻倍,甚至百倍的提升。一般来说,一个查询越复杂,所需维护的内部状态也就更复杂,状态占用空间也更大。例如在测试中,q4, q7, q9, q18, q20 这些查询的内部状态都接近或者超过 20GB,而 q102 与q105 内部状态接近 10GB。此时对于内部状态的存取往往成为计算过程的瓶颈,需要流计算引擎对状态进行缓存、索引等优化加速。从测试结果来看,RisingWave 相比于 Flink 很显然能够更好的支持复杂的流处理任务。

RisingWave 为什么能够达到如此高的性能?

RisingWave 的性能与其设计实现密不可分。总结下来,我认为 RisingWave 的性能优势得益于三个关键点:

  1. RisingWave 是由 Rust 从头开发,且几乎不依赖于任何第三方 JVM 组件。相比于由 Java开发的 Flink 来说,从语言上就有巨大优势。当然,该优势主要存在于计算层,因为 Flink所使用的 RocksDB 存储使用的是 C++开发;
  2. 作为大数据项目,Flink 拥有类似于 MapReduce 样式的 Java API 层,而 Flink SQL 实质上是对 Flink Java API 进行的封装。计算机基础理论告诉我们,封装越多,性能越差。相比之下,RisingWave 并无中间表达层,因此可以直接针对 SQL 进行高度定制化优化,从而实现性能大幅提升;
  3. Flink 直接使用 RocksDB 来存储计算中间状态,RocksDB 对计算并无感知。相反,RisingWave 内部自己实现了存储,存储能够感知计算,并且使用远端存储(例如 S3、HDFS 等)来大幅降低存储成本,从而实现计算性价比提升。

事实上,除了这三点之外,还有很多因素可能导致 RisingWave 相比于 Flink 的性能优势。例如,Flink 目前的发展方向是成为大一统平台,其批处理、机器学习、数据湖等功能的引入会轻易提升系统复杂性,从而带来不必要的性能开销。总的来说,关于性能这一块,我们可以单独写新文章介绍,感兴趣的朋友也可以关注微信公众号:RisingWave 中文开源社区进行深入了解。

没有被测量的部分

复杂无状态计算

正如上面提到的,当处理复杂无状态计算时,理论来说由 Rust 开发的系统性能应该远高于基于 JVM 语言编写的系统。考虑到如今不少应用都需要在流处理系统内部进行 json 解析、字符串处理等复杂操作,我们的确会考虑在今后添加测试该类查询。

多流 join

多流 join 是流处理中的一个非常典型的场景。当用户有多个数据源时(例如多个 MySQL 实例或者多个 Kafka topic),便会考虑使用流处理系统进行 join 来进行分析。在处理多流 join 的过程城中,往往都会需要面临内部状态过大的问题。根据 Nexmark 基准测试的结果,我们已经初步验证了 RisingWave 在内部状态较大情况下的出色性能,而多流 join 很显然是RisingWave 所擅长的。事实上,我们已经开始了对多流 join 的实验,根据初步实验结果,RisingWave 可以轻松处理十个以上数据流的 join,而相比之下,Flink 往往会直接因为状态管理问题而崩溃。我们会逐步将多流 join 的实验结果公布给大家。

UDF、水位线等高级功能

流处理系统的精湛之处远远不至于处理聚合、join、窗口等算子。事实上,用户经常需要 UDF或者水位线等进阶功能来扩展表达能力,以及保证系统正确性。然而,限于 Nexmark 基准测试的局限性,我们并没有对这些功能进行测试。我们会考虑在今后的测试中涵盖这些功能。

如何对 Flink 进行性能调优?

Flink 性能调优不仅是个技术活,更是个经验活。RisingWave 团队在 Flink 性能调优方面积累了将近一年的经验。总结下来,我们可以通过三个方面对 Flink 进行性能优化:

  • 部署调优。Flink 支持 Kubernetes 部署。然而,由于对于 JVM 生态的重度依赖,通过Kubernetes 部署 Flink 并不能直接得到最佳性能。我们往往需要考虑 Flink 的具体部署方式(比如如何部署 Zookeeper 节点等等)。
  • SQL 调优。Flink 使用 Calcite 进行 SQL 解析与规划,但由于对数据缺少感知,Flink 并不能很好的进行 SQL 优化。因此,在一些查询中,我们需要手动改写 SQL 实现更高性能。
  • RocksDB 调优。由于 Flink 使用 RocksDB 进行内部状态管理,而 RocksDB 作为存储层并不感知计算,因此用户往往需要手工调节 RocksDB 参数来实现最佳性能。值得注意的是,RocksDB 本身就拥有数百个可调参数,希望调好 RocksDB 还得对 RocksDB 的内部结构进行非常深入的研究。

如何对 RisingWave 进行性能调差?

最有可能降低 RisingWave 性能的方式在于调小计算节点的内存,并通过大量不规则访问来使RisingWave 出现大量缓存未命中。其原理是:RisingWave 使用远端存储来维护内部状态,并使用计算节点缓存最常访问的远端存储上的状态。当计算节点的容量较小,并访问不规则的情况下,就可能欺骗到缓存策略(RisingWave 使用的是基于 LRU 的算法),从而让RisingWave 频繁访问远端存储。远端存储访问往往代价较大,频繁访问注定会造成整体性能下降。

总结

在本文,我们描述了基于 Nexmark 基准测试进行的 Flink 和 RisingWave 性能对比。诚然,单一的基准测试并不能够覆盖流处理系统的方方面面,但是我们仍然可以大致了解到 Flink 与RisingWave 在常用场景下的性能区别以及原因。除流处理能力以外,Flink 有着诸多特性值得用户探索(包括批处理、机器学习、StateFun 等等),而 RisingWave 则会更加专注于优化流处理方面的性能效率。

性能并不是评价系统优劣的唯一标准,但的确我们一直都在为实现极致性能而努力。

关于 RisingWave

RisingWave 是一款分布式 SQL 流处理数据库,旨在帮助用户降低实时应用的的开发成本。作为专为云上分布式流处理而设计的系统,RisingWave 为用户提供了与 PostgreSQL 类似的使用体验,并且具备比 Flink 高出 10 倍的性能以及更低的成本。了解更多:

GitHub: risingwave.com/github

官网: risingwave.com

公众号: RisingWave 中文开源社区