如何选择合适的 Diskless Kafka

文章导读

随着越来越多企业将 Kafka 迁移至云原生架构,AutoMQ 正逐渐成为 Kafka 用户的云端优选。作为兼容 Apache Kafka 协议、专为云设计的新一代发行版,AutoMQ 凭借高性能、弹性扩展和极致成本等优势,在全球范围内的热度持续攀升,GitHub Star 数也顺势突破 8k 大关。在海外社区涌现的众多讨论与推文中,我们发现了这样一篇来自开发者的深度好文,将其内容翻译并呈现给大家,概述如下。

Apache Kafka 虽已成为流数据领域的事实标准,但其诞生于 IDC 时代的"存算一体"架构在云原生环境下正显露疲态:高昂的跨可用区(Cross-AZ)流量成本与难以解耦的存算资源,成为企业数字化基础设施中不可忽视的隐形债务。伴随着云存储技术的成熟,Diskless Kafka 逐渐成为下一代消息中间件演进的必然趋势。

在这场架构变革中,我们不仅将深入剖析Diskless Kafka兴起的根本原因,更将目光投向目前唯一的开源、成熟的Diskless Kafka方案------AutoMQ。以此为切入点,重点探讨 Kafka 向基于共享存储架构演进的技术路径,并将从各个维度深度剖析 Diskless Kafka 设计背后的核心权衡,助您充分理解不同架构的优劣,从而选择出真正适合自己的 Kafka 方案。

引言

自问世以来,Apache Kafka 已确立了其作为分布式消息领域"事实标准"的地位,支撑着全球无数企业的关键业务------从微服务通信到实时分析,应用场景无处不在。

然而,它的架构诞生于本地数据中心(On-Premise)主导的时代。在那时,服务器硬件通常需要预先采购,且网络带宽远不及今日。当这种设计理念被移植到现代云环境时,弊端便显露无疑:跨可用区(Cross-AZ)的网络流量成本飙升,且难以实现计算与存储的独立扩展。

这一现状正推动着整个行业向一种全新的范式演进:Diskless Kafka。在本文中,我们将首先Diskless Kafka趋势,并盘点市场上现有的解决方案。随后,我们将重点剖析 AutoMQ------作为业内最早尝试实现 Diskless Kafka 的先行者之一。

Diskless Kafka vs Apache Kafka

Kafka 诞生于十多年前的 LinkedIn,旨在为生产者(Producer)与消费者(Consumer)提供一种高效的解耦手段;双方均通过 Broker 进行交互以传递消息。正如前文所述,Kafka 诞生的时代背景具有以下特征:

  • 主要依赖本地数据中心(IDC),而非云服务。
  • 那时的网络带宽相当有限;因此,构建系统的标准做法是将计算与存储紧密绑定在一起(即存算一体)。

基于上述背景,Kafka 的 Broker 被设计为将消息直接持久化存储在本地磁盘上,并通过 Broker 间的消息复制机制来实现数据冗余与高可用性。

这意味着,扩容存储就必须增加机器节点。这种机制迫使用户即便在现有计算资源利用率并不高的情况下,也不得不配置额外的 CPU 和内存。

除了资源效率低下的问题,Broker 级别的数据复制在云端多可用区(AZ)部署中还会带来巨大的、往往被忽视的财务黑洞。这种成本主要体现在以下两个方面:

  1. 生产者流量成本:在一个典型的跨三个可用区部署的高可用架构中,生产者必须将消息发送给指定分区的 Leader Broker。如果 Kafka 集群将 Leader 分区均匀分布在三个可用区,那么大约有三分之二的情况下,生产者会将消息发送到位于不同可用区的 Broker 上(从而产生跨区流量费用)。
  1. 复制流量成本:当 Leader 节点接收到数据后,为了保证数据的持久性,必须将其复制到位于另外两个可用区的 Follower 节点。这一过程会引发规模更为庞大的跨可用区数据传输,导致同一份消息数据产生"二次"网络费用。

鉴于上述痛点,各类采用全新架构设计的系统正应运而生。

Diskless Kafka

尽管 Kafka 存在上述不足,但其 API 无疑已大获全胜。它不仅是数据流领域的行业标准,更衍生出了一个极为庞大且成熟的生态系统。

因此,任何厂商若想提供更优的替代方案,其首要前提必须是兼容 Kafka。推倒重来去构建一套全新的系统并非良策,重构 Kafka 的存储层才是更为高效的路径。

所谓Diskless Kafka架构(Diskless Architecture),指的是一种将所有消息彻底从 Broker 中剥离,并转而全量存储于对象存储(Object Storage)的架构模式。

这种新模式彻底重塑了 Kafka 兼容系统在云端的运作机制,其带来的收益不仅立竿见影,更具颠覆性:

  • 成本优势:相比传统 Kafka Broker 所必需的高性能块存储,对象存储的单位容量(Per GB)成本要低整整一个数量级。
  • **弹性伸缩:**Broker 节点转变为无状态的计算单元,可根据处理需求灵活进行扩缩容;与此同时,存储容量则完全依托于对象存储,能够独立、自动地进行扩展。
  • **持久性与可用性:**云对象存储服务天生具备极高的持久性,并能自动在多个可用区之间复制数据。这种高可靠性主要得益于纠删码(EC)技术与自动数据复制机制的结合,且这些机制通常天然具备跨多可用区的能力。由于数据保护的重任已完全下沉由存储层接管,系统不再需要维护昂贵且复杂的 Broker 级数据复制,从而彻底根除了与之伴生的跨可用区流量难题。

值得特别指出的是,Diskless Kafka架构与 Kafka 分层存储(KIP-405)所提出的分层架构有着本质区别。KIP-405 引入的是一套双层存储体系:

  • 本地存储(Broker 本地磁盘):用于存储最新的数据。
  • 远程存储(S3/GCS/HDFS):用于存储历史数据。

然而,在这种架构下,Broker 无法实现彻底的无状态化,我们前文讨论过的种种痛点依然存在。

从 WarpStream、BufStream 到 Aiven,各路厂商纷纷基于这一理念推出了 Kafka 的替代方案。这类平台的集中爆发,恰恰印证了其所解决的痛点是何等关键。虽然它们殊途同归,都致力于通过对象存储来实现降本与弹性增强,但各家的技术成色与实现路径却不尽相同。

在本文中,我将重点剖析 AutoMQ------相较于其他竞品,它提供了一种独树一帜的 Diskless Kafka 解法。

AutoMQ

100% Kafka 兼容与开放性

正如我们之前所探讨的,新一代系统必须严格遵循 Kafka 协议。

Kafka 协议是围绕本地磁盘构建的。从向物理日志追加消息,到通过定位分段文件(Segment Files)中的偏移量(Offset)来服务消费者,所有的操作逻辑都紧紧围绕着这一设计核心。

即便如此,基于对象存储构建 Kafka 兼容方案仍面临巨大挑战。暂且不论性能,对象存储的写入机制与磁盘迥异。我们无法像操作文件系统那样,打开一个不可变对象并直接在末尾追加数据。

对此,部分厂商(如 WarpStream、Bufstream)选择另起炉灶,开发一套新协议来兼顾:

  • 适配对象存储
  • 提供 Kafka 兼容性

他们认为,相较于基于开源 Kafka 协议进行改造,这种方式更为直接。然而,此举也带来了严峻挑战:难以紧跟社区演进的步伐,往往导致某些 Kafka API 特性的支持滞后甚至缺失。例如,WarpStream 就耗费了相当时日才补齐了对事务(Transactions)的支持。

AutoMQ 并不认可这种路径。

AutoMQ 选择了一条不同的路:完整复用了除存储层以外的所有 Kafka 上层逻辑。团队投入了大量精力,为 Kafka 量身打造了一款全新的存储引擎,它既能与对象存储无缝对接,又能向上提供 Kafka 协议运行所必需的底层抽象。

得益于此,AutoMQ 有底气为其Diskless Kafka方案承诺 100% 的 Kafka 兼容性;即便 Kafka 社区后续推出了诸如队列(queues)等前沿新特性,AutoMQ 也能通过合并上游代码,实现无缝集成与同步支持。

AutoMQ 的另一大亮点在于其开源属性。这赋予了用户极大的自由度------既可以尝鲜试用,也能在自家环境中独立部署。

放眼当下的市场,它是唯一一款兼具开源与生产级可用性的 Diskless Kafka 解决方案。反观其他开箱即用的竞品,无一例外均采用了闭源策略,而 Kafka 社区官方关于Diskless Kafka Topic 的提案(KIP: Diskless Topic)尚处于讨论之中,远未落地。

绝不以牺牲低延迟为代价

向对象存储写入数据的速度,无疑要慢于本地磁盘。一些 Diskless Kafka 方案选择了牺牲低延迟性能:它们必须等到消息在对象存储中完成持久化后,才会向生产者返回确认(ACK)。

然而,这种方案伴随着严重的代价。当延迟出现数量级级别的恶化时,客户端往往需要投入额外的时间重新打磨配置,涵盖从并发度到缓存大小的方方面面(关于缓存,后文会有更多讨论)。在金融等对延迟极度敏感的关键业务场景中,这种程度的性能退化往往是不可接受的。

AutoMQ 拒绝这种妥协。

为此,他们借鉴了数据库领域的一个经典理念:预写日志(Write Ahead Log, WAL)。这是一种专用于崩溃恢复与事务恢复的"仅追加"(Append-only)日志结构。其原理十分简单:所有的数据变更,必须先被完整记录在日志中,随后才能被应用到数据库的实际数据文件中。

遵循这一原则,即便系统在事务提交之后、变更尚未刷入数据文件之前发生崩溃,系统依然可以通过读取 WAL 来重放(Replay)这些变更。这对于数据库管理系统(DBMS)确保数据的持久性至关重要。

回到 AutoMQ 的架构设计上来,每个 Broker 都配备了一个 WAL,其底层依托于 AWS FSx 或其他云厂商提供的同类高性能存储服务。正是凭借这些通常具备跨可用区复制能力的强健共享服务,AutoMQ 能够确保从容应对可用区(AZ)级别的故障。

当 Broker 接收到消息时,会先将其写入内存缓冲区,待数据成功持久化至 WAL 后,便立即向生产者返回确认响应(ACK)。通过这种机制,客户端无需等待消息写入对象存储(这一相对缓慢的过程),从而显著降低了延迟。

随后,这些消息会被打包,通过异步方式批量刷写(Flush)至对象存储中。

相较于等待一批消息被完整写入对象存储,在消息持久化至 WAL(磁盘)后立即发送 ACK 响应,无疑要快得多。

需要补充说明的一点是,由于磁盘设备主要承担 WAL 的职能以确保消息的持久性,系统对磁盘空间的需求极小。AutoMQ 默认将 WAL 的大小设定为 10GB 即可满足需求。

基于 Leader 与无 Leader 架构之争

究其核心,Apache Kafka 是一个基于 Leader(Leader-Based)的系统。对于 Topic 的每一个分区,通常都配备了一个 Leader 以及零个或多个 Follower。所有的写入操作都必须流向该分区的 Leader,而读取请求则可以由 Leader 或该分区的 Follower 来承接。AutoMQ 依然沿用了这一架构路线。

在Diskless Kafka架构下,鉴于所有 Broker 均共享底层的对象存储,Bufstream 和 WarpStream 等厂商认为,传统的"基于 Leader(Leader-Based)"架构已非必需。

相反,他们将所有 Broker 视为一个同构的、无状态的计算资源池;也就是说,任意 Broker 均可接收针对任意分区的写入请求。业界通常将这种模式称为"无 Leader(Leaderless)架构"。

接下来,我们将从多个维度深入探讨,为您剖析这两种架构设计背后的权衡与取舍。

额外的组件

为了实现无 Leader 架构,相较于原生 Kafka 方案,系统在部署时必须引入一个额外的组件。由于每个 Broker 都能处理读写请求,因此必须依靠协调器(Coordinator)来为客户端指派具体的 Broker、管理元数据,并重新实现那些原本由分区 Leader 掌控的所有 Kafka 高级特性。

然而,这种对外部协调器的依赖也带来了一些副作用。它引入了 Broker 自身之外的外部依赖,从而使数据写入链路变得更加复杂。同时,这也推高了维护 Kafka API 兼容性的成本,因为诸如事务或幂等生产者等 Kafka 核心特性,都必须在协调器的深度参与下进行彻底的重新实现。

AutoMQ 坚持采用基于 Leader 的架构,因此无需引入额外的"协调器"组件,其消息生产与消费机制依然完美沿袭了 Kafka 的原生逻辑。客户端会向 Bootstrap Broker 发起元数据请求,以获知 Broker 列表、所在的可用区(AZ)以及各 Topic 分区的 Leader 信息。在生产数据时,客户端会始终尝试与指定 Topic 分区的 Leader 进行交互;而在消费端,客户端既可以连接 Leader,也可以连接任意副本节点。

由于 AutoMQ 完整保留了 Leader 这一核心概念,因此系统架构中无需任何额外的组件介入。

写入灵活性

无 Leader 架构赋予了写入端极大的灵活性。

其显著优势之一在于大幅削减了跨可用区(Cross-AZ)传输的成本。系统能够无缝地将流量从生产者路由至与其位于同一可用区的 Broker,从而避免了跨区流量费用的产生。

AutoMQ 基于 Leader 的架构凭借共享对象存储的能力,同样能够轻松规避写入侧的跨可用区流量。这主要涵盖两种场景:

  • 如果 Leader 与生产者处于同一可用区:太棒了,这是最理想的情况,生产者只需照常向该 Broker 发送消息即可。
  • 如果 Leader 位于不同的可用区:当生产者请求目标 Broker 信息以发送消息时,服务发现机制不会返回位于异地的 Leader 地址,而是会返回一个与生产者处于同一可用区的 Broker 地址。

该同区 Broker 会先将接收到的消息写入对象存储的临时文件中。随后,Leader 会"认领"这些临时文件,并将数据正式写入实际的分区位置。之所以采取这种机制,是因为在基于 Leader 的架构中,所有针对分区的最终写入操作,必须由 Leader 亲自经手。

凭借这一设计,AutoMQ 在彻底消除跨可用区流量费用的同时,并未牺牲 Kafka 的兼容性(因为 Leader 依然把控着分区数据的写入权)。

读取侧的数据局部性

在 AutoMQ 这类基于 Leader 的系统中,分区 Leader 拥有得天独厚的优势:极高的数据局部性(Data Locality)

由于 Leader 统管其名下分区的所有写入操作,那些最新生成且被频繁访问的"热数据(Hot Data)"可以被直接驻留在其本地内存缓存中。

谈及缓存,它堪称Diskless Kafka架构中的"生命线"。毕竟,直接从对象存储读取数据的性能表现,终究无法与本地磁盘相提并论。

除去性能层面的考量,过于频繁的读取请求还会导致成本激增,毕竟云服务商通常是依据对象存储的 GET 请求次数来进行计费的。而在这一语境下,缓存机制不仅是提升性能的关键,更是兼顾成本效益(降本增效)的利器。

这不仅有助于提升读取性能,还能最大化数据在上传至对象存储前的批处理效率。

正是基于这一架构优势,AutoMQ 顺理成章地设计出了双层缓存机制:利用专用的日志缓存(Log Cache)来应对写入与热点读取,同时配备块缓存(Block Cache)来服务于历史数据。

反之,无 Leader 架构则可能受制于数据局部性(Data Locality)较低的困境。

当任意 Broker 随时都能向同一分区写入数据时,该分区的数据就会被打散,以碎片化的形式分布在 S3 的大量小对象中,而这些对象又是由不同的 Broker 各自生成的。

尽管这些对象最终会被合并,但在初始阶段,Broker 仍不得不发起大量的 GET 请求,去拉取那些零散分布的对象以响应消费者。

缓存固然能缓解这一压力。但核心难题在于:在无 Leader 架构下,既然所有 Broker 都能承接读取请求,该如何制定高效的数据缓存策略?

据我了解,为了解决这一问题,厂商们试图将分区"绑定"给特定的 Broker。例如,WarpStream 利用一致性哈希算法将分区分配给特定的 Broker,由该节点全权负责指定分区的缓存与数据服务。

这种做法实际上是变相回归了"基于 Leader"的架构理念,但同时也为此引入了额外的复杂性。由于缺乏本地数据支持,为了填补由此产生的性能与成本缺口,工程团队不得不设计各种变通方案,以规避对象存储的高延迟与昂贵的 API 调用成本(如 S3 GET 请求)。

例如,WarpStream blog 中曾详细阐述了他们如何利用 mmap 技术来最小化 S3 API 的开销。而这,恰恰是为了缓解因无法实现真正的数据局部性而不得不付出的设计代价。

元数据管理

基于 Leader 与无 Leader 架构之间的分歧,不仅停留在表面,更深入到了元数据管理的底层逻辑。在 AutoMQ 的基于 Leader 模型中,元数据管理显得大道至简,因为它直接复用了 Kafka 成熟的分区逻辑。

当 AutoMQ 写入数据时,它会像原生 Kafka 那样,直接将数据写入一个已经开启的分区。这种设计使得元数据的存储与组

相关推荐
b***66611 小时前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
大吱佬1 小时前
GO 八股整理(自用)
开发语言·后端·golang
i***11861 小时前
springboot使用redis
spring boot·redis·后端
aiopencode1 小时前
苹果应用商店上架的系统逻辑,从产品开发到使用 开心上架 上架IPA 交付审核流程
后端
h***38181 小时前
SpringBoot - Cookie & Session 用户登录及登录状态保持功能实现
java·spring boot·后端
摇滚侠1 小时前
零基础小白自学Git_Github教程,GitHubDeskTop安装,笔记10
笔记·git·github
苏三说技术1 小时前
索引夺命10连问,你能顶住第几问?
后端
摇滚侠2 小时前
零基础小白自学 Git_Github 教程,GitHub 是如何工作的,笔记08
笔记·git·github
j***48542 小时前
idea创建SpringBoot自动创建Lombok无效果(解决)
spring boot·后端·intellij-idea