对比Pulsar和Kafka,对Pulsar的性能有一个更准确的看法
李鹏辉/郭思洁
注:这篇文章介绍了StreamNative对Confluent最近发表的文章《Apache Kafka、Apache Pulsar和RabbitMQ的基准测试:哪个最快?””.关于这些系统的简要概述,请参阅我们对Pulsar与Kafka的评论(第一部分,第二部分)。
执行摘要
今天,许多公司都在关注实时数据流应用,以开发新产品和服务。企业必须首先了解不同事件流系统的优势和差异,然后才能选择最适合的技术来满足其业务需求。
基准是组织用来比较和衡量不同技术性能的一种方法。为了使这些基准有意义,它们必须做得正确并提供准确的信息。不幸的是,由于任何数量的问题,基准很容易不能提供准确的洞察力。
Confluent最近进行了一项基准测试,以评估Kafka、Pulsar和RabbitMQ在吞吐量和延时方面的比较。根据Confluent的博客,Kakfa能够以 '低延迟 '实现 '最佳吞吐量',RabbitMQ能够以 '较低的吞吐量 '提供 '低延迟'。总的来说,他们的基准测试宣布Kafka在 '速度 '方面是明显的赢家。
虽然Kafka是一项成熟的技术,但Pulsar是当今许多公司选择的顶级流媒体技术,从全球企业到创新的初创公司。事实上,在最近的Splunk峰会conf20上,Splunk的首席产品官Sendur Sellakumar讨论了他们采用Pulsar而不是Kafka的决定。
'......我们已经转移到Apache Pulsar作为我们的底层流。这是我们对企业级多租户流媒体长期架构的赌注。'
- Sendur Sellakumar, CPO, Splunk
这只是众多采用Pulsar的公司的例子之一。这些公司之所以选择Pulsar,是因为它能够在现代弹性云环境(如Kubernetes)中以水平和成本效益的方式扩展到大量数据,而且没有单点故障。同时,自动数据再平衡、多租户、地理复制和无限保留的分层存储等内置功能,简化了操作,使团队更容易专注于业务目标。
归根结底,开发者采用Pulsar是因为它的功能、性能,以及上文提到的Pulsar的所有独特方面,使它非常适合成为流媒体数据的主干。
了解了我们所知道的,我们不得不仔细看看Confluent的基准,试图了解他们的结果。我们发现有两个问题是非常有问题的。首先,也是最大的不准确来源,是Confluent对Pulsar的了解有限。由于不了解这项技术,他们无法以能够准确测量Pulsar性能的方式设置测试。
第二,他们的性能测量是基于一组狭窄的测试参数。这限制了结果的适用性,未能为读者提供有关这些技术在不同工作负载和真实世界使用情况下的能力的准确描述。
为了向社会提供一个更准确的情况,我们决定解决这些问题并重复测试。主要的更新包括。
- 我们更新了基准设置,以包括Pulsar和Kafka支持的所有耐久性级别。这使我们能够在相同的耐用性水平上比较吞吐量和延迟。
- 我们修复了OpenMessaging Benchmark(OMB)框架,以消除使用不同实例引入的变体,并纠正了其OMB Pulsar驱动程序中的配置错误。
- 最后,我们测量了额外的性能因素和条件,如不同数量的分区和混合工作负载,其中包括写、尾随阅读和补读,以提供一个更全面的性能视图。
在进行了这些更新之后,我们又重新进行了测试。结果是--在更接近真实世界工作负载的场景中,Pulsar的表现明显优于Kafka,并且在Confluent使用的基本场景中与Kafka的表现相匹配。
下面的部分强调了最重要的发现。在StreamNative基准测试结果一节中,更全面的性能报告也给出了我们测试设置的细节和附加评论。
StreamNative基准测试结果亮点
#1 在与Kafka相同的耐久性保证下,Pulsar实现了605MB/s的发布和端到端吞吐量(与Kafka相同),以及3.5GB/s的追读吞吐量(比Kafka高3.5倍)。增加分区的数量和改变耐久性水平对Pulsar的吞吐量没有影响。然而,在改变分区数量或改变耐久性水平时,Kafka的吞吐量受到了严重影响。
表1:Pulsar和Kafka在不同工作负载下的吞吐量差异,不同的耐久性保证
持久性水平 |
隔断 |
Pulsar |
Kafka |
|
峰值发布+尾随阅读吞吐量(MB/s |
1级耐用性 |
1 |
300 MB/s |
160 MB/s |
100 |
300 MB/s |
420 MB/s |
||
2000 |
300 MB/s |
300 MB/s |
||
2级耐用性 |
1 |
300 MB/s |
180 MB/s |
|
100 |
605 MB/s |
605 MB/s |
||
2000 |
605 MB/s |
300 MB/s |
||
峰值追读吞吐量(MB/s |
1级耐用性 |
100 |
1.7 GB/s |
1 GB/s |
2级耐用性 |
100 |
3.5 GB/s |
1 GB/s |
关于 'Level-1持久性 '的细节,请参见《分布式系统中的持久性概述》一节,详细讨论Pulsar和Kafka的持久性差异。
#2:在每个不同的测试案例中,Pulsar提供的延迟明显优于Kafka(包括不同的订阅数量、不同的主题数量和不同的耐久性保证)。
Pulsar的第99个百分点的延迟在5到15毫秒之间。Kafka的第99百分位数延迟可以达到几秒钟,并且受到主题数量、订阅和不同耐久性保证的巨大影响。
表2:Pulsar和Kafka之间的端到端P99延迟,不同数量的订阅有不同的耐久性保证
分区和订阅 |
本地耐用性 |
复制的持久性 |
Pulsar |
Kafka |
|
端到端P99延迟(ms (出版+尾声阅读) |
100个分区,1个订阅 |
同步 |
Ack-1 |
5.86 |
18.75 |
Ack-2 |
11.64 |
64.62 |
|||
异步 |
Ack-1 |
5.33 |
6.94 |
||
Ack-2 |
5.55 |
10.43 |
|||
100个分区,10个订阅 |
同步 |
Ack-1 |
7.12 |
145.10 |
|
Ack-2 |
14.65 |
1599.79 |
|||
异步 |
Ack-1 |
6.84 |
89.80 |
||
Ack-2 |
6.94 |
1295.78 |
表3:不同主题数量的Pulsar和Kafka之间的端到端P99延迟,有不同的耐久性保证
本地耐用性 |
复制的持久性 |
隔断 |
脉冲星 |
卡夫卡 |
|
端到端P99延迟(ms (出版+尾声阅读) |
同步 |
Ack-1 |
100 |
5.86 |
18.75 |
5000 |
6.26 |
79236 |
|||
10000 |
6.67 |
187840 |
|||
Ack-2 |
100 |
11.64 |
64.62 |
||
5000 |
14.38 |
157960 |
|||
10000 |
15.78 |
197140 |
|||
异步 |
Ack-1 |
100 |
5.33 |
6.94 |
|
5000 |
5.75 |
86641 |
|||
10000 |
6.64 |
184513 |
|||
Ack-2 |
100 |
5.55 |
10.43 |
||
5000 |
6.20 |
116028 |
|||
10000 |
7.50 |
200793 |
3:Pulsar提供的I/O隔离度明显优于Kafka。当有消费者追读历史数据时,Pulsar的第99百分位发布延迟保持在5毫秒左右。相比之下,Kafka的延迟受到了追读的严重影响。Kafka的第99百分位数发布延迟可以从几毫秒增加到几秒钟。
表4:Pulsar和Kafka之间的P99发布延迟与追赶式读取
当地的耐用性 |
复制的持久性 |
脉冲星 |
卡夫卡 |
|
发布P99延迟(ms (混合工作量) |
同步 |
Ack-1 |
5.89 |
13.48 |
Ack-2 |
15.39 |
2091.31 |
||
异步 |
Ack-1 |
10.44 |
9.51 |
|
Ack-2 |
35.51 |
1014.95 |
我们所有的基准都是开源的,所以好奇的读者可以自己重复测试。此外,你可以深入挖掘结果和指标,这些都可以在资料库中找到。
尽管我们的基准比Confluent的更准确、更全面,但它并没有涵盖所有的情况。归根结底,任何基准都不能取代在你自己的硬件上用你自己的工作负载进行的测试。我们鼓励你评估更多的变量和场景,并使用你自己的设置和环境进行测试。
深入了解Confluent基准测试
Confluent使用OpenMessaging Benchmark(OMB)框架作为他们的基准测试的基础,并做了一些修改。在本节中,我们将描述我们在Confluent的基准中发现的问题,并解释这些问题是如何影响Confluent的测试结果并导致错误的结论的。
关于Confluent设置的问题
Confluent的基准测试的一个根本问题是Pulsar没有被正确设置。(我们将在StreamNative基准测试一节中进一步讨论这个问题)。)除了调整Pulsar的问题之外,Pulsar和Kafka的设置也有不同的耐久性保证。由于持久性水平会影响性能,所以两个系统的持久性设置必须相等,这样的比较才有意义。
Confluent的工程师使用了Pulsar的默认耐久性保证,这比Kafka使用的配置要强得多。由于增加耐久性会对延迟和吞吐量产生负面影响,Confluent的测试对Pulsar的要求比对Kafka的要求高得多。在Confluent使用的Pulsar版本中,还不支持将耐久性降低到与Kafka相当的水平,但这样的未来将作为Pulsar的一部分在即将发布的版本中发布,并在本次测试中使用。如果他们的工程师在两个系统上都使用这种新的等效耐久性设置,测试结果就可以进行准确的比较。我们当然不会因为Confluent的工程师没有使用一个尚未发布的功能而责怪他们,然而,这篇报道没有为这些结果提供必要的背景,而是把它们当作等同的结果,这种额外的背景将在这里提供。
关于OMB框架的问题
Confluent的基准测试遵循OMB框架指南,该指南建议在多个事件流系统中使用同一实例类型。然而,在我们的测试中,我们发现同一类型的不同实例之间存在大量的差异,特别是在磁盘IO方面。为了尽量减少这种差异,我们在Pulsar和Kafka的运行中使用了相同的实例,我们发现这大大有助于提高结果的准确性,因为即使磁盘IO性能的微小变化也会导致整个系统性能的更大差异。我们建议在未来更新OMB框架指南,以包括这一建议。
Confluent方法学的问题
Confluent的基准只测量了一些有限的场景。例如,现实世界的工作负载包括写、尾部阅读和追赶式阅读。尾部读取发生在消费者读取接近日志 '尾部 '的最近消息时,这也是Confluent测试的唯一场景。相比之下,追赶式读取是指消费者有大量积压的信息,它必须消耗这些信息来 '追赶 '日志的尾部,这在现实世界的系统中是一项常见的关键任务。如果不考虑追赶式读取,会严重影响写和尾部读取的延时。由于Confluent的基准只关注吞吐量和端到端的延迟,它不能完整地描述各种工作负载下的预期行为。同样,为了进一步给出更接近真实世界使用情况的结果,我们也认为用不同数量的订阅和分区来运行该基准是很重要的。很少有组织只关心少数几个主题和少数几个分区和消费者,他们需要有大量不同的消费者和大量不同的主题/分区的能力,以映射到他们的业务用例中。
综上所述,我们在下表中概述了Confluent公司方法的具体问题。
表5:Confluent的基准方法的问题
测试的参数 |
不包括的内容 |
限制条件 |
写入和尾随读出 |
补读 |
虽然最大吞吐量和端到端延迟有助于说明事件流系统的基本性能特征,但将研究局限于两个参数,只能提供系统性能的部分观点。 |
1订阅 |
不同数量的订阅者/消费者群体 |
没有显示订阅的数量如何影响吞吐量和延迟。 |
100个分区 |
不同数量的分区 |
没有显示分区的数量如何影响吞吐量和延迟。 |
Confluent的基准测试的许多问题都源于对Pulsar的有限理解。为了帮助其他人在未来运行基准时避免这些问题,我们将提供一些关于该技术的见解。
了解Pulsar的耐久性保证是运行准确基准的必要条件,所以我们将从这里开始讨论。我们将从分布式系统中的耐久性的一般概述开始,然后解释Pulsar和Kafka所提供的耐久性保证之间的差异。
分布式系统中的耐用性概述
持久性是指在面对外部问题时保持系统的一致性和可用性,如硬件或操作系统故障。单节点存储系统(如RDBMS)将 'fsync '写入磁盘以确保最大的耐久性。操作系统通常会对写入进行缓冲,在发生故障时可能会丢失,但fsync将确保这些数据被写入物理存储中。在分布式系统中,耐用性通常来自于复制,数据的多个副本被分发到不同的节点上,这些节点可以独立发生故障。然而,重要的是不要把本地耐久性(fsyncing数据)和复制耐久性混为一谈,因为它们都有不同的目的。在下面的章节中,我们将解释这些功能之间的一些关键区别以及为什么两者都很重要。
复制耐久性和局部耐久性
分布式系统通常同时提供复制耐久性和本地耐久性。每种类型的耐久性都有单独的机制来控制。你可以在不同的组合中使用这些机制来设置所需的耐久性水平。
复制的持久性是通过使用一种算法来创建数据的多个副本来实现的,因此相同的数据可以存储在几个地方,以提高可用性和可访问性。复制的数量N决定了系统的故障容忍度,许多系统需要一个 '法定人数',或N/2+1个节点,来确认一个写入。一些系统提供了在任何一个副本仍然可用的情况下继续为现有数据服务的能力。这种复制机制是处理一个实例完全丢失的关键,新的实例能够从现有的副本中重新复制数据,同时也是可用性和共识的关键(这不在本讨论范围内)。
相比之下,本地耐用性决定了确认在单个节点层面上的意义。这就需要将数据sync到一个持久的媒介上,以确保没有数据丢失,即使发生断电或硬件故障。数据的fsync确保在发生瞬时故障时,在机器可以恢复的情况下,节点拥有它以前的确认的所有数据。
持久性模式。同步与非同步
不同类型的系统提供不同程度的耐久性保证。一般来说,任何给定的系统所能提供的整体耐久性水平取决于以下几点。
- 系统是否将数据同步到本地磁盘上
- 系统是否将数据复制到多个地点
- 当系统确认复制到一个对等体时
- 当系统确认写给客户的内容时
在不同的系统中,这些选择差别很大,并不是所有的系统都能让用户选择控制这些数值,但一般来说,缺乏其中一些机制的系统(如非分布式系统中的复制)提供的耐久性较差。
为了更具体地讨论这个问题,我们可以定义两种耐久性模式,控制系统何时确认写入,包括内部复制和客户端。这两种模式是 '同步 '和 '异步'。这些模式的操作如下所述。
- 同步持久性。系统只有在数据被成功同步到本地磁盘(本地耐用性)或复制到多个位置(复制耐用性)之后,才会向对等体/客户返回写响应。
- 异步持久性。系统在数据成功同步到本地磁盘(本地耐用性)或复制到多个位置(复制耐用性)之前,向对等体/客户返回一个写响应。
耐用性水平。衡量耐久性的保证
耐久性保证可以有多种形式,并取决于以下变量。
- 无论数据是存储在本地,还是复制在多个地点,或者两者都有
- 写入被确认的时间(同步与非同步)。
和耐久性模式一样,我们定义了一些耐久性 '级别',我们可以用它来区分不同的分布式系统。我们定义了四个级别。表6描述了每个级别,从最高的耐久性级别到最低的。
表6:分布式系统的耐用性等级
级别 |
复制 |
本地 |
运作 |
1 |
同步 |
同步 |
只有在数据被复制到多个(至少是大部分)地点,并且每个副本都被成功地同步到本地磁盘之后,系统才会向客户返回一个写入响应。 |
2 |
同步 |
异步 |
系统只有在数据被复制到多个(至少是大部分)位置后才会向客户返回写响应。不能保证每个副本都成功地同步到本地磁盘上。 |
3 |
异步 |
同步 |
当一个副本被成功地fsync到本地磁盘时,系统会向客户返回一个写入响应。并不保证数据被复制到其他位置。 |
4 |
异步 |
异步 |
在数据被复制到多个地点后,系统会立即向客户端返回一个写入响应。没有复制或本地耐久性保证。 |
大多数分布式关系数据库管理系统(如NewSQL数据库)保证了最高级别的耐久性;因此,它们将被归类为一级。
与数据库一样,Pulsar是一个1级系统,默认提供最高级别的耐用性。此外,Pulsar允许选择为每个应用程序单独定制所需的耐久性级别。相比之下,大多数Kafka的生产部署被配置为2级或4级系统。根据我们对Kafka有限的了解,Kafka可以通过设置flush.messages为1和flush.ms为0来配置为1级系统。但配置这两个设置对吞吐量和延迟都有严重影响。我们将在我们的基准测试结果中详细讨论。
我们将从Pulsar开始,详细了解各家的耐用性能力。
Plusar的耐用性
Pulsar在所有层面上都提供耐久性保证。它可以将数据复制到多个地点,并将数据同步到本地磁盘上。Pulsar有两种耐久性模式(前面描述的同步和异步)。每个选项都是可以单独配置的。你可以在不同的组合中使用它们,为个人使用情况定制设置。
Pulsar使用一个相当于筏子的、基于法定人数的复制协议来控制复制的耐久性。你可以通过调整ack-quorum-size和write-quorum-size参数来调整复制的耐久性模式。这些参数的设置在下面的表7中描述。Pulsar所支持的耐用性水平在下面的表8中描述。(对Pulsar的复制协议和共识算法的详细讨论超出了本文的范围;但是,我们将在未来的博文中深入探讨这些领域)。)
表7:Pulsar中的耐用性配置设置
配置设置 |
持久性模式 |
|
复制 |
ackQuorumSize = 1 |
异步 |
ackQuorumSize ≥ writeQuorumSize / 2 + 1 |
同步 |
|
当地 |
(默认) journalWriteData = true journalSyncData = true |
同步 |
journalWriteData = true journalSyncData = false |
异步 |
|
journalWriteData = false journalSyncData = false |
异步 |
表8:Pulsar的耐用性水平
持久性水平 |
复制的持久性 |
当地的耐用性 |
1级 |
同步。 ackQuorumSize ≥ writeQuorumSize / 2 + 1 |
同步。 journalWriteData = true journalSyncData = true |
3级 |
异步。 ackQuorumSize = 1 |
同步。 journalWriteData = true journalSyncData = true |
2级 |
同步。 ackQuorumSize ≥ writeQuorumSize / 2 + 1 |
异步。 journalWriteData = true journalSyncData = false |
4级 |
异步。 ackQuorumSize = 1 |
异步。 journalWriteData = true journalSyncData = false |
2级 |
同步。 ackQuorumSize ≥ writeQuorumSize / 2 + 1 |
异步。 journalWriteData = false journalSyncData = false |
4级 |
异步。 ackQuorumSize = 1 |
异步。 journalWriteData = false journalSyncData = false |
Pulsar通过将数据写入和/或同步到一个或多个日志磁盘来控制本地耐久性。Pulsar还提供了使用表9中的配置参数来调整本地耐久性模式的选项。
表9:Pulsar的本地持久性模式参数
参数 |
描述 |
价值观 |
日志写数据 |
控制是否在将数据持久化到分类账磁盘之前将数据写入其日记账磁盘。 |
true = 启用日记功能 false= 禁用日记功能 |
journalSyncData |
控制在向经纪人返回写确认之前是否将数据同步到日志磁盘上 |
true = 启用fsync false= 禁用fsync |
Kafka的持久性
Kafka提供了三个耐久性级别。1级、2级和4级。Kafka可以在第2级(默认设置)提供复制的耐用性,但在第4级不提供耐用性保证,因为它缺乏在确认写入前将数据同步到磁盘的能力。Kafka可以通过设置flush.messages为1,flush.ms为0来配置为Level 1系统运行。
Kafka的ISR复制协议控制着复制的耐久性。你可以通过调整与该协议相关的acks和min.insync.replicas参数来调整Kafka的复制耐久性模式。这些参数的设置在下面的表10中描述。Kafka支持的耐久性级别在下面的表11中描述。(对Kafka的复制协议的详细解释超出了本文的范围;但是,我们将在未来的博文中探讨Kafka的协议与Pulsar的协议有什么不同)。
表10:Kafka中的耐用性配置设置
配置设置 |
持久性模式 |
|
复制 |
acks = 1 |
异步 |
acks = all |
同步 |
|
当地 |
默认的fsync设置 |
异步 |
flush.messages = 1 flush.ms = 0 |
同步 |
表11:Kafka的耐用性等级
持久性水平 |
复制的持久性 |
当地的耐用性 |
第2级 |
同步。 acks = all |
异步。 默认的fsync设置 |
4级 |
异步。 acks = 1 |
异步。 默认的fsync设置 |
第1级 |
同步。 acks = all |
同步。 flush.messages = 1 flush.ms = 0 |
4级 |
异步。 acks = 1 |
同步。 flush.messages = 1 flush.ms = 0 |
与Pulsar不同,Kafka不会将数据写入单独的日志磁盘。相反,Kafka在将数据写入磁盘之前会确认写入。这种操作最大限度地减少了写和读之间的I/O争夺,并防止性能下降。
Kafka确实提供了在每个消息之后进行fsync的能力,上面的flush.messages = 1和flush.ms = 0,虽然这可以用来大大降低消息丢失的可能性,然而它严重影响了吞吐量和延迟,这最终意味着这样的设置在生产部署中很少使用。
Kafka无法对数据进行日志记录,这使得它在机器故障或断电的情况下容易出现数据丢失。这是一个显著的弱点,也是腾讯选择Pulsar作为其新计费系统的主要原因之一。
Pulsar和Kafka之间的耐用性差异
Pulsar的耐久性设置是高度可配置的,允许用户优化耐久性设置,以满足个别应用、用例或硬件配置的要求。
由于Kafka提供的灵活性较低,根据不同的场景,并不总是能够在两个系统中建立同等的耐久性设置。这使得基准测试变得困难。为了解决这个问题,OMB框架建议使用最接近的可用设置。
有了这个背景,我们现在可以描述Confluent的基准中的差距。Confluent试图模拟Pulsar的fsyncing行为。在Kafka中,Confluent选择的设置提供了异步持久性。然而,他们为Pulsar选择的设置提供了同步耐久性。这种差异产生了有缺陷的测试结果,不准确地将Pulsar的性能描述为较差。当我们稍后回顾自己的基准测试结果时,你会看到,Pulsar的性能与Kafka一样好,甚至更好,同时提供了更强的耐久性保证。
流媒体基准测试
为了更准确地了解Pulsar的性能,我们需要解决Confluent基准测试中的问题。我们重点调整了Pulsar的配置,确保两个系统的耐用性设置相当,并包括额外的性能因素和条件,如不同的分区数量和混合工作负载,使我们能够衡量不同使用情况下的性能。下面的章节详细解释了我们所做的改变。
流媒体设置
我们的基准设置包括Pulsar和Kafka支持的所有耐久性级别。这使得我们能够在相同的耐用性水平上比较吞吐量和延迟。我们使用的耐用性设置如下所述。
复制的持久性设置
我们的复制耐久性设置与Confluent的相同。虽然我们没有做任何改动,但为了完整起见,我们在表12中分享了我们使用的具体设置。
表12:复制耐用性设置设置
持久性模式 |
配置 |
|
脉冲星 |
同步 |
ensemble-size=3 Write-quorum-size=3 ack-quorum-size=2 |
异步 |
ensemble-size=3 Write-quorum-size=3 ack-quorum-size=1 |
|
卡夫卡 |
同步 |
replicas=3 acks=all min.insync.replicas=2 |
异步 |
replicas=3 acks=1 min.insync.replicas=2 |
一个新的Pulsar功能让应用程序可以选择跳过日志,这放宽了本地耐久性保证,避免了写放大,并提高了写吞吐量。(这个功能将在Apache BookKeeper的下一个版本中提供)。)然而,这个功能不会被作为默认功能,我们也不建议在大多数情况下使用,因为它仍然会带来消息丢失的可能性。
我们在基准测试中使用了这个功能,以确保两个系统之间的性能比较准确。绕过Pulsar上的日志,可以提供与Kafka默认的fsync设置相同的本地耐久性保证。
Pulsar的新功能包括一个新的本地耐久性模式(Async - Bypass journal)。我们使用这种模式来配置Pulsar,以匹配Kafka默认的本地耐久性水平。表13显示了我们基准测试的具体设置。
表13:StreamNative的基准测试的本地耐久性设置设置
持久性模式 |
配置 |
|
Pulsar |
同步(默认)。 |
journalWriteData=true journalSyncData=true journalMaxGroupWaitMSec=1 |
异步 (写给日记) |
journalWriteData=true journalSyncData=false journalMaxGroupWaitMSec=1 journalPageCacheFlushIntervalMSec=1000 |
|
异步 (绕过日记) |
journalWriteData=false journalSyncData=false |
|
Kafka |
同步 |
flush.messages=1 flush.ms=0 |
异步(默认)。 |
flush.messages=10000(默认)。 flush.ms=1000(默认)。 |
StreamNative框架
我们修复了Confluent’的OMB框架中的一些问题,并纠正了其OMB Pulsar驱动中的配置错误。我们开发的新的基准测试代码,包括下面描述的修正,可以作为开放源代码使用。
OMB框架中的修正
Confluent遵循OMB框架的建议,使用两套实例,一套用于Kafka,另一套用于Pulsar。在我们的基准测试中,我们分配了一组三个实例以消除变化。在我们的第一次测试中,我们在Pulsar上部署了所有三个实例。然后,我们用同一组实例在Kafka上重复进行测试。
由于我们使用相同的机器对不同的系统进行基准测试,我们在每次运行前都清除了文件系统的分页缓存。这确保了当前的测试不会受到以前活动的影响。
在OMB Pulsar驱动配置中的修复问题
我们修复了Confluent的OMB Pulsar驱动配置中的一些错误。下面的章节解释了我们对经纪人、bookie、生产者、消费者和Pulsar图像所做的具体改动。
Broker的变化
Pulsar的broker使用managedLedgerNewEntriesCheckDelayInMillis参数来确定追赶式订阅在向其消费者发送消息之前必须等待的时间长度(以毫秒为单位)。在OMB框架中,这个参数的值被设置为10。这是Confluent的基准测试不准确地显示Pulsar的延迟高于Kafka的主要原因。我们把这个值改为0,以模拟Kafka在Pulsar上的延迟行为。在做了这个改变之后,Pulsar在所有的测试案例中都显示出明显比Kafka好的延迟。
此外,为了优化性能,我们将bookkeeperNumberOfChannelsPerBookieparameter的值从16增加到64,以防止经纪人和赌客之间的任何单一Netty通道成为瓶颈。当大量的消息积聚在Netty的IO队列中时,这种瓶颈会导致高延迟。
我们打算在Pulsar文档中更清楚地提供这一指导,以帮助那些希望完全优化端到端延迟的用户。
Bookie变化
我们增加了一个新的bookie配置,以测试Pulsar在绕过日记时的性能。关于这个问题的讨论,请看耐久性部分,记得有了这个功能,我们更接近于Kafka的耐久性保证。
为了测试这一功能的性能,我们在官方发布的Pulsar 2.6.1版本的基础上建立了一个定制的图像,以包括这一变化。(更多细节,请参见Pulsar图像)。
我们手动配置了以下设置,以绕过Pulsar的日记。
journalWriteData=false
journalSyncData=false
此外,我们将journalPageCacheFlushIntervalMSec参数的值从1改为1000,以衡量Pulsar中的异步本地耐久性(journalSyncData=false)。增加该值使Pulsar能够模拟Kafka的冲刷行为,如下所述。
Kafka通过将文件系统页面缓存中的脏页冲到磁盘上来确保本地的耐久性。数据是由一组名为pdflush的后台线程刷新的。Pdflush是可配置的,刷新之间的等待时间通常设置为5秒。将Pulsar的journalPageCacheFlushIntervalMSec参数设置为1000,相当于Kafka上5秒的pdflush时间间隔。做出这样的改变使我们能够更精确地对异步本地耐久性进行基准测试,并在Pulsar和Kafka之间实现更精确的比较。
生产者的变化
我们的批处理配置与Confluent的相同,只有一个例外。我们增加了切换时间间隔,使其长于批处理时间间隔。具体来说,我们把 batchingPartitionSwitchFrequencyByPublishDelay参数的值从1改为2。这个变化确保了Pulsar的生产者在每个批处理期间只关注一个分区。
将切换间隔和批处理间隔设置为相同的数值,会导致Pulsar切换分区的频率超过需要,从而产生过多的小批处理,可能会影响吞吐量。让切换间隔大于批处理间隔可以将这种风险降到最低。
消费者的变化
当应用程序无法以足够快的速度处理传入的消息时,Pulsar客户端使用接收器队列来施加反向压力。消费者接收队列的大小可以影响端到端的延迟。一个较大的队列可以比一个较小的队列预取和缓冲更多的消息。
两个参数决定了接收队列的大小:receiverQueueSize和maxTotalReceiverQueueSizeAcrossPartition。Pulsar计算接收队列大小的方法如下。
Math.min(receiverQueueSize, maxTotalReceiverQueueSizeAcrossPartitions / number of partitions)
例如,如果maxTotalReceiverQueueSizeAcrossPartitions被设置为50000,而你有100个分区,那么Pulsar客户端就会把每个分区上消费者的接收队列大小设置为500。
对于我们的基准,我们将maxTotalReceiverQueueSizeAcrossPartitions从50000增加到5000000。这种调整优化确保了消费者不会施加背压。
Plusar图像
我们建立了一个定制的Pulsar版本(v.2.6.1-sn-16),包括上述的Pulsar和BookKeeper修复。2.6.1-sn-16版本是基于官方的Pulsar 2.6.1版本。
StreamNative方法学
我们更新了Confluent的基准测试方法,以使用真实世界的工作负载来获得更全面的性能视图。具体来说,我们为测试做了以下改动。
- 增加了补读,以评估以下内容。每个系统在处理补读时能达到的最大吞吐量水平读取如何影响发布和端到端的延迟
- 改变分区的数量,以观察每个变化对吞吐量和延迟的影响。
- 变化了订阅的数量,看看每个变化对吞吐量和延迟有什么影响
我们的基准方案测量了以下类型的工作负载。
- 最大吞吐量。每个系统能达到的最大吞吐量是多少?
- 发布和尾随阅读延迟。在给定的吞吐量下,每个系统能达到的最小发布和端到端尾随延迟水平是什么?
- 追加阅读。当从大量积压的信息中读取时,每个系统能达到的最大吞吐量是多少?
- 混合工作负载。当消费者在追赶时,每个系统能达到的最小发布和端到端尾随延迟水平是什么?追赶式读取如何影响发布延迟和端到端尾随延迟?
测试平台
OMB框架推荐了特定的测试平台定义(用于实例类型和JVM配置)和工作负载驱动配置(用于生产者、消费者和服务器端)。我们的基准使用与Confluent相同的测试平台定义。这些测试平台的定义可以在Confluent的OMB资源库中找到我们的分叉。
下面,我们强调我们观察到的磁盘吞吐量和磁盘同步延迟。在解释基准结果时,这些硬件指标是很重要的。
磁盘吞吐量
我们的基准使用了与Confluent相同的实例类型,即i3en.2xlarge(有8个vCores,64GB内存,2 x 2,500GB NVMe SSD)。我们确认,i3en.2xlarge实例可以支持两个磁盘上高达655MB/s的写入吞吐量。见下面的dd结果。
第1盘
dd if=/dev/zero of=/mnt/data-1/test bs=1M count=65536 oflag=direct
65536+0条记录在
65536+0条记录流出
复制了68719476736字节(69GB),210.08秒,327MB/s
第2盘
dd if=/dev/zero of=/mnt/data-2/test bs=1M count=65536 oflag=direct
65536+0条记录在
65536+0条记录流出
复制了68719476736字节(69GB),209.635秒,328MB/s
磁盘数据同步延时
在运行与延迟有关的测试时,捕捉NVMe SSD上的fsync延迟是至关重要的。我们观察到,这3个实例的第99个百分点的fsync延迟从1毫秒到6毫秒不等,如下图所示。正如前面提到的,我们看到不同实例的磁盘存在大量的差异。这主要表现在这个延迟上,我们发现有一组实例表现出一致的延迟。
图14:3个不同实例上的第99百分位数fsync延迟
流媒体基准测试结果
我们在下面总结了我们的基准结果。你可以找到我们完整的基准报告。
最大吞吐量测试
最大吞吐量测试的目的是确定每个系统在处理包括发布和尾随阅读的工作负载时,在不同的耐久性保证下可以达到的最大吞吐量。我们还改变了主题分区的数量,以观察每个变化对最大吞吐量的影响。
我们发现
- 当配置为提供1级耐久性(同步复制耐久性和同步本地耐久性)时,Pulsar实现了~300 MB/s的吞吐量,达到了日志磁盘带宽的物理极限。Pulsar是在可扩展的耐用日志存储(Apache BookKeeper)之上实现的,以最大限度地利用磁盘带宽而不牺牲耐用性保证。Kafka在100个分区的情况下能够达到~420MB/s。需要注意的是,在提供一级耐用性时,Pulsar被配置为使用一个磁盘作为写的日志磁盘,另一个磁盘作为读的分类账磁盘,而Kafka则使用两个磁盘进行写和读。虽然Pulsar的设置能够提供更好的I/O隔离,但其吞吐量也受到单个磁盘的最大带宽(约300MB/s)的限制。其他的磁盘配置可以为Pulsar带来好处,并且可以实现更有成本效益的操作,这将在以后的博文中讨论。
- 当配置为提供2级耐用性(同步复制耐用性和异步本地耐用性)时,Pulsar和Kafka的最大吞吐量都达到了约600 MB/s。两个系统都达到了磁盘带宽的物理极限。
- Kafka在一个分区上的最大吞吐量只有Pulsar最大吞吐量的一半。
- 改变分区的数量对Pulsar的吞吐量没有影响,但它确实影响了Kafka的吞吐量。
- 当分区数量从100个增加到2000个时,Pulsar保持了最大的吞吐量(在1级耐用性保证下约300MB/s,在2级耐用性保证下约600MB/s)。
- 当分区的数量从100个增加到2000个时,Kafka的吞吐量减少了一半。
发布和端到端延时测试
发布和端到端延迟测试的目的是确定每个系统在处理由发布和尾随阅读组成的工作负载时,在不同的耐久性保证下所能达到的最低延迟。我们改变了订阅的数量和分区的数量,以观察每个变化对发布和端到端延迟的影响。
我们发现,
- 在所有的测试案例中,Pulsar的发布和端到端延迟都明显低于Kafka的延迟(高达数百倍),这些案例评估了各种耐久性保证和不同数量的分区和订阅。即使分区数量从100个增加到10000个,或者订阅数量从1个增加到10个,Pulsar的第99百分位发布延迟和端到端延迟也保持在10毫秒以内。
- Kafka的发布和端到端延迟受到了订阅和分区数量变化的极大影响。
- 随着订阅数量从1增加到10,发布和端到端的延迟都从~5毫秒增加到~13秒。
- 随着主题分区的数量从100个增加到10000个,发布和端到端的延迟都从~5毫秒增加到~200秒。
追赶阅读测试
补读测试的目的是确定每个系统在处理只包含补读的工作负载时能达到的最大吞吐量。在测试开始时,一个生产者以每秒200K的固定速度发送消息。当生产者发送了512GB的数据后,消费者开始读取已经收到的信息。消费者处理累积的信息,并不难跟上生产者的速度,生产者继续以同样的速度发送新的信息。
在处理补读时,Pulsar的最大吞吐量比Kafka的快3.5倍。Pulsar的最大吞吐量达到了3.5GB/s(350万条信息/秒),而Kafka的吞吐量只有1GB/s(100万条信息/秒)。
混合工作负荷测试
这个混合工作负载测试是为了确定在混合工作负载中追赶式读取对发布和尾随式读取的影响。在测试开始时,生产者以每秒200K的固定速率发送消息,消费者以尾随模式消耗消息。在生产者产生512GB的消息后,它将启动一组新的追赶式消费者,从头开始读取所有的消息。同时,生产者和现有的尾随读取的消费者继续以相同的速度发布和消耗消息。
我们使用不同的耐久性设置测试了Kafka和Pulsar,发现追读严重影响了Kafka的发布延迟,但对Pulsar影响不大。Kafka的第99百分位数发布延迟从5毫秒增加到1-3秒。然而,Pulsar保持了99%的发布延迟,从几毫秒到几十毫秒不等。
总结
基准的一个棘手的方面是,它们往往只代表业务逻辑和配置选项的狭窄组合,可能反映也可能不反映现实世界的用例或最佳实践。基准可能会因为其框架、设置和方法的问题而进一步受到影响。我们注意到最近Confluent基准中的所有这些问题。
应社区的要求,StreamNative的团队开始运行这个基准,以便为Pulsar的真正性能能力提供知识、见解和透明度。为了运行一个更准确的基准,我们确定并修复了Confluent基准的问题,同时还增加了新的测试参数,以深入了解这些技术在更多真实世界的使用案例中的比较。
对我们的基准测试结果显示,在与Kafka相同的耐久性保证下,Pulsar能够在类似于真实世界用例的工作负载中胜过Kafka,并在Confluent的有限用例中实现与Kafka相同的端到端通过。此外,在每个不同的测试案例中,Pulsar都能提供明显优于Kafka的延迟,包括不同的订阅、主题和耐久性保证,以及比Kafka更好的I/O隔离。
如前所述,任何基准都不能取代在你自己的硬件上用你自己的工作负载进行的测试。我们鼓励你使用自己的设置和工作负载来测试Pulsar和Kafka,以了解每个系统在你的特定生产环境中的表现。