Kafka与Pulsar架构对比:存储计算耦合与分离的选型指南

发布时间:2026/10/15 4:37:25
Kafka与Pulsar架构对比:存储计算耦合与分离的选型指南 做消息中间件选型这件事我最怕的就是上来就比性能。尤其是这几年云原生概念普及之后到处都能看到“万亿级数据吞吐”“毫秒级低延迟实时处理”这种话术好像只要从Kafka换到Apache Pulsar所有问题都会自动消失。事实当然没那么简单。这篇文章我会从架构本质出发把这两个主流消息中间件的关键差异掰开揉碎讲清楚Kafka的存储计算耦合到底卡在哪Pulsar的存储计算分离又解决了什么以及真实压测和调优里那些文档不会写的参数细节。无论你是在准备Kafka到Pulsar的迁移还是要为一个新平台做技术选型这篇文章都可以当一个参考坐标——数字可以被压测工具粉饰架构的取舍骗不了人。1. 别急着对比Kafka和Pulsar先回答万亿级吞吐到底是谁的需求1.1 消息中间件在云原生架构里的真实定位十年前消息中间件解决的最核心问题是系统解耦和削峰填谷。订单进来先丢进队列下游慢慢消费大家各自为政互不拖累。但到了云原生阶段消息中间件的角色早就变了它既是微服务之间通信的骨干网也是实时数据管道的数据源入口还要承担事件驱动架构里那根“事件总线”。流计算引擎要从里面捞数据数据湖的增量同步要从里面读变更AI特征的实时更新也要靠它。在这种定位下消息中间件本身也变成了云原生体系里的一个“有状态基础设施”它必须能跟随业务流量一起弹性伸缩、随时扩容、按需计费。问题来了到底什么才算“支持弹性”不是说你能在配置中心里改两个参数就叫弹性而是当流量翻倍时你的节点能够快速增加并真正分摊压力当流量回落时节点能安全缩回去。Kafka和Pulsar在这条路上的设计哲学完全不同。1.2 吞吐、延迟、持久性一个不可能三角先定优先级很多人问我Kafka和Pulsar到底哪个吞吐高、哪个延迟低我的回答通常是反过来的你先告诉我你的业务更怕丢数据还是更怕慢消息中间件的核心指标有三个——吞吐量、端到端延迟、持久性保障它们构成一个典型的不可能三角。任何一次写入你要在“多快返回确认”和“数据多安全落盘”之间做权衡。Kafka生产端默认的ack1是写入leader内存页缓存就返回吞吐很高但leader节点一宕机就可能丢数据ackall虽然安全吞吐立刻掉一截。Pulsar这里的选项变成了写入BookKeeper的确认数量你要在写入副本数actor和延迟之间做选择。先定清这个优先级再选型不然很容易被“单机百万条每秒”这种数字误导。1.3 多租户、弹性伸缩、跨可用区容灾是云原生新增的隐形成本过去自建机房时代一个公司可能就一套Kafka集群所有业务线共用谁嗓门大谁占资源。云原生时代背景下多个业务线、多个环境、多个租户在同一个集群上运行是常态。Kafka的隔离手段主要靠topic级别的配额限制但配额的管理粒度粗也不够动态Pulsar这套体系在namespace级别内置了存储配额、消息速率限制、订阅数量限制配置一次自动执行。还有跨可用区容灾。一个集群跨三个AZ部署已经是很多企业级系统的默认要求。Kafka做跨机房复制要单独部署MirrorMaker或者自己搭一套跨集群复制通道运维成本不低。Pulsar原生支持geo-replication在namespace层面配置复制策略就行。所以我说云原生带来了一堆新增的必答题多租户隔离、弹性伸缩、跨AZ容灾这些能力在对比性能数字之前应该先放在桌面上谈清楚。2. Kafka的强与弱分区模型带来的扩展天花板2.1 分区是Kafka的命根子也是它的锁链Kafka的高吞吐秘密几乎全部建立在分区模型上。一个topic被拆成多个partition每个partition内部保证消息有序数据以append-only的方式追加到日志文件里。写就是顺序写磁盘读就用页缓存加速配合零拷贝技术把数据直接从磁盘映射到网卡发送出去。这套设计在机械硬盘时代就拿到了极致的顺序IO效率放到今天SSD上更是如鱼得水。但分区的另一面是它是扩展性的基本单位也是运维噩梦的来源。分区数决定了一个topic最大能做到多少并行度生产者往不同分区并发写消费者按分区分配消费任务每个分区的数据只能被同一个消费组里的一个消费者实例处理。所以想要更高的消费并行度就得增加分区数。而分区数一多后面跟着的连锁反应马上就来。2.2 分区数翻倍之后文件句柄、Rebalance、故障恢复一起变重我实际数过一次一个常规的Kafka集群分区数从1000加到10000最直观的变化有几个。第一每个分区的leader副本和follower副本都会在broker上维护对应的日志文件和索引文件文件句柄数量随分区数线性增长再加上索引文件、时间戳索引文件光文件描述符就能吃满进程限制。第二分区数多了之后某台broker一旦宕机涉及它上面几千个分区的leader切换加上ISR收缩、副本重新同步恢复时间从分钟级直接拉到小时级高峰期根本等不起。更头疼的是Rebalance。消费组内的实例数量发生变化上线、下线、崩溃触发Rebalance时整个消费组会停止消费重新分配分区。分区越多Rebalance耗时越长而Rebalance期间消费lag就快速上涨这在实时性要求高的场景里很难接受。2.3 存储计算耦合的真正代价迁移分区等于搬数据Kafka的broker节点既要负责读写请求的处理又要负责数据的实际存储两者完全绑定。这种架构下想要扩容比如从3个broker加到6个broker只能靠分区迁移——把部分分区的数据复制到新节点上。听起来简单实际操作起来麻烦得很几千个分区的数据迁移要持续数小时甚至数天迁移期间对集群的CPU、磁盘IO、网络带宽都有明显冲击如果业务高峰期做扩容还可能把正在写数据的节点拖垮。这就是Kafka“存储计算耦合”的代价。你要增加计算能力就必须带着数据搬一次家你要缩小集群还得带着数据再搬一次。所以在Kafka集群运维圈子里有个顺口溜式的经验能不上新节点就不上新节点能在原节点上加规格就加规格因为加节点一时爽数据重平衡火葬场。3. Pulsar的存储计算分离架构调整背后是一套新的伸缩逻辑3.1 Broker无状态化扩容从搬数据变成加节点Pulsar架构上最本质的区别是把消息数据的存储从broker中彻底剥离了出来。你写入的消息实际保存在一组独立的BookKeeper节点上broker只处理请求路由、缓存和元数据管理自身不持久化消息数据。换句话说Pulsar的broker是无状态的。无状态带来一个直接的好处当流量涨起来想加broker就加新broker加进来之后自动从负载均衡器那里接管一部分topic的分区不需要搬移任何已有的消息数据。扩容的操作从“搬数据”变成了“加节点”时间从小时级缩短到分钟级。反过来业务低峰期想缩容直接把多余broker下线就行数据一个字节都不会丢。这个能力在云环境里尤其贴合按量付费的思路你甚至可以按业务日周期动态调整计算节点数。3.2 BookKeeper的分段式存储写入放大与故障恢复的取舍Pulsar依赖的BookKeeper存储层也有自己的一套设计逻辑。一个topic的消息不是存储在一个巨大的日志文件里而是被切分成一连串的segment段每个segment包含若干个entry。写入时数据先追加到当前活跃的segment当一个segment大小达到阈值就封存再开启新的segment。封存过的segment可以分布在多个BookKeeper节点上这就让读取不再受单个节点的容量和IO限制消费时甚至可以并行读取多个segment的数据。段式存储对故障恢复的意义很大。某个BookKeeper节点损坏时只需把它上面存活的segment从其他节点的副本恢复出来不需要重建整段连续日志恢复速度明显快于复制整个分区的模式。这里也要提一句Pulsar的写入确认和副本策略是可配置的写入时可以选择写入1个副本就返回也可以选择写满3个副本再返回前者延迟更低后者安全性更高。这个参数直接影响“高吞吐”和“不丢数据”之间的天平具体怎么选回到第1章说的先定清需求。3.3 游标与消费进度的存储位置容易被忽略的关键差异很多从Kafka转过来的人第一次用Pulsar都会困惑消费组的offset到底存在哪里Kafka里消费进度写在一个内部的__consumer_offsets主题里消费者提交位移本质上也是往这个topic发消息依赖它来做消费组恢复和重平衡。Pulsar选择了另一条路消费游标cursor存储在BookKeeper里并且以游标为单位管理每个订阅的消费位置。这个差异看似不起眼实际影响不小。Pulsar的订阅模型支持exclusive、shared、failover三种模式shared模式下可以多个消费者实例并行消费同一个topic的不同消息消费进度按游标精确管理允许消息的乱序确认和堆积管理。对于流计算这类高吞吐实时处理场景Pulsar的游标模型不需要像Kafka那样反复平衡分区消费侧可以更细粒度地控制并行度这在系统调优时是一个很大的自由度。3.4 容器化和编排Pulsar与云原生的贴合度为什么更高这几年我观察到一个现象凡是认真做了容器化改造的团队对Pulsar的接受度普遍比传统运维团队高。原因是Pulsar的broker无状态化让它天然适配Kubernetes的Deployment模型想扩副本就扩副本想滚动升级就滚动升级存储层独立为StatefulSet各司其职。Kafka在容器环境里则比较尴尬因为broker本身带了数据Pod重建就意味着数据重新同步通常需要配置复杂的本地持久化卷和严格的调度规则。Pulsar这边还有一个叫“负载均衡器”的内置组件会持续监控各broker的资源使用情况自动把负载过高的topic转移到空闲broker上这在容器环境中能有效应对Pod漂移带来的资源不均衡。所以说Pulsar并不是在所有维度上都比Kafka好但在云原生部署这个语境下它的架构确实更加贴合。4. 压测实录在同一目标下两类集群的极限和瓶颈在哪里4.1 压测拓扑与目标设定不对比空跑指标只对比业务模型我做过一些相对真实的对比测试建议所有人都先厘清一件事单纯跑性能压测的数值没什么参考价值因为不同参数的配置对结果影响太大。我们的做法是模拟一个具体的业务模型每秒100万条消息写入、每条消息1KB、3个副本持久化、消费端需要做到接近实时的数据读取。然后分别用Kafka集群和Pulsar集群去扛这个负载看它们的吞吐上限、延迟表现和资源消耗。先交代一下集群配置为了对比公平两边计算节点都用同规格的虚拟机Kafka部署3个brokerPulsar部署3个broker加3个BookKeeper节点存储都挂NVMe SSD网络都是万兆。在这种近似对称的资源条件下测出来的差异基本可以归因于架构本身。4.2 Pulsar侧最容易踩的几个参数坑Pulsar的压测过程中我们踩过不少坑有几个参数是尤其值得注意的第一broker内存配置。Pulsar的broker使用内存作为缓存如果堆内存设置太小缓存命中率直线下降读取延迟飙升。但我们曾经把一个broker的堆内存调得很大结果GC停顿频繁反而拖垮了写入性能。实际调优需要在内存缓存和GC开销之间找一个平衡点。第二IO线程和CPU线程的比例。broker默认的线程池大小在低负载下够用一旦压到百万级消息每秒出现大量的连接空闲等待就要手动调大ioThreads和processorThreads。第三写入确认策略。Pulsar的持久化可以配置为写入1个BookKeeper节点即返回WriteQuorum1也可以配置为写入2个或3个节点后再返回。WriteQuorum3时的写入吞吐会比1时低不少但换来的是更高的数据安全等级。这个参数必须和业务需求匹配否则要么白牺牲性能要么压测看着好看到生产就出问题。第四个坑跟磁盘有关BookKeeper的journal写日志和ledger存储最好放在不同的物理磁盘上因为journal要求低延迟顺序写ledger存储要求大容量顺序写混在一起会导致同时读写时互相抢占IO延迟抖动特别明显。我们第一次压测时没注意这个细节结果Pulsar的P99延迟一直不稳定拆盘之后立刻改善。4.3 Kafka侧在超大规模分区下的资源配置规律Kafka这边的调优路径相对成熟但也有一些容易被忽视的坑。分区数在压测中的规律大致是这样的当分区数从100加到500吞吐量明显提升因为并行度上来了加到1000以上吞吐增长速度放缓而broker的文件句柄数、内存占用、网络连接数都在同步上涨超过3000分区后吞吐几乎没有增长反而Rebalance和故障恢复的成本开始拖后腿。还有一个明显的现象Kafka的消费端吞吐对分区数很敏感。消费者实例数超过分区数后多余实例完全空闲消费并行度被分区数锁死。所以当我们压Kafka的高吞吐实时处理场景时需要预先估算好分区数和消费者实例数的合理配比避免资源浪费。4.4 对比结果里最值得关注的三个现象在我们跑完一组完整的压测之后有三个现象给我留下的印象最深。第一在分区数相对少50个以内时Kafka的端到端延迟表现确实非常优秀单分区的顺序写模式让它的写入路径极短延迟可以稳定在个位数毫秒。Pulsar由于多了BookKeeper一层写入链路在同样小分区场景下的延迟略高一些但在可接受范围。第二一旦分区数扩大Kafka的延迟分位数会明显上翘P99延迟抖动加大主要原因是分区多之后磁盘IO和网络连接开始竞争页缓存命中率下降。Pulsar在分区数从50扩展到500时延迟保持了很好的稳定性这得益于broker和存储层的解耦。第三故障恢复场景下的差距比常规读写更明显。我们在压测中手动kill掉一个broker节点Kafka集群有大约几百个分区的leader需要切换并重新复制恢复耗时约15分钟Pulsar这边受影响的分区被负载均衡器自动转移到其他broker存储层的segment大部分已在其他副本存在恢复时间大约是Kafka的三分之一。5. 低延迟实时链路的全链路调优从producer到consumer逐个拆5.1 端到端延迟的三个构成段发送、存储复制、消费拉取讲低延迟优化首先得把“延迟”拆开。端到端延迟指的是从生产者调用send方法开始到消费者真正拿到这条消息之间的时间大致由三段构成发送段生产者把消息发给broker等待broker确认。Kafka和Pulsar在这一段的差异不大都受网络往返时间和客户端配置影响。存储段broker收到消息后写入存储并按配置复制副本。Kafka是写入本地日志并同步副本Pulsar是写入BookKeeper并确认副本数。这一段是二者延迟差异的主要来源。拉取段消费者向broker发起拉取请求broker返回可消费的消息。Kafka使用长轮询机制消费者有新的拉取请求后维持较长时间的连接数据一就绪就立即返回Pulsar的消费者连接broker后会维持一个流式读取消息到达后持续推送。两者的实时性差异不大但参数配置不同。5.2 批量和低延迟的博弈linger.ms、batch.size和同步发送的取舍生产端的延迟调优本质上是和批量发送做一场交易。Kafka的Producer默认会把多条消息攒在一起发送通过batch.size和linger.ms两个参数控制攒批策略。batch.size决定缓冲区能装多少字节linger.ms决定消息在缓冲区等待多久再发送。曾经有个团队为了提高吞吐把linger.ms调到了100ms结果下游实时数据的刷新延迟增加了100ms业务根本接受不了。如果你的场景是低延迟优先在Kafka这边建议把linger.ms设置为5ms以内batch.size设置到64KB到256KB之间不要盲目放大。同步发送future.get()会让发送延迟完全暴露在业务调用链路上延迟波动更大异步发送加回调是最优解。Pulsar生产端的设计也类似可以设置pending queue size如果这个值太小发送线程会频繁阻塞吞吐上不去如果太大会导致内存压力上升延迟反而变高。这组参数需要在压测里反复试没有绝对推荐值。5.3 消费端的Pipelining与Ack顺序实时计算场景必须注意消费端对延迟的影响经常被低估。很多人以为只要数据进来了消费者拿到就算实时但实际上消费端的提交模式和拉取参数直接决定了端到端链路能压到多低。Kafka消费端有两个关键参数要关注fetch.min.bytes和fetch.max.wait.ms。前者是拉取的最小字节数后者是如果数据量没到阈值时最长等待时间。实时场景建议把fetch.min.bytes设小一点比如1字节这样broker一有数据就会返回而不是攒到一定量才返回fetch.max.wait.ms则关系到空转时的轮询频率通常在500ms左右。Pulsar消费端则要关注receiverQueueSize这是消费者本地预取消息的数量预取越大消费吞吐越高但消息一旦发给消费者除非用unacknowledged消息重投机制否则很难保证精确的一次处理顺序。对于强实时且需要精确处理的链路建议把预取队列调小宁可牺牲一点吞吐换来更可控的分发逻辑。还有一个经验值得单独提一下在实时计算的场景里消费端的处理流程最好是“一条消息处理完确认一条”不要批量确认。批量确认虽然吞吐高但只要中间某条消息处理失败重投的粒度会变大可能带来重复计算的范围扩大。我们用Pulsar做实时特征计算时就踩过这个坑把批确认改成每消息确认后才彻底稳定下来。5.4 一个真实链路调优的参考顺序低延迟实时链路的优化不是一次完成的我通常按下面的顺序逐步进行第一步先调生产端。确认ack方式Kafka推荐ack1且开启幂等Pulsar设置WriteQuorum和AckQuorum并保持默认的延迟优化配置确保常规写入路径没有额外等待。第二步调broker端。Kafka检查页缓存和磁盘IO绑核避开GC导致的抖动Pulsar检查IO线程池和journal与ledger分离情况。第三步调消费端。把拉取等待时间、预取队列控制在低延迟范围并确保消费处理逻辑本身没有阻塞操作。第四步观测P99延迟和消费lag。用消费lag直接反映整条链路是否积压如果lag持续增长说明某个环节的处理能力跟不上这时先放大单个环节的容量而不是继续死磕参数。6. 选型落地经验不是Kafka不行而是场景变了6.1 生态成熟度和运维工具链的现实差距抛开架构原理选型最终要回到现实你的团队能不能维护这套系统Kafka作为最主流的消息中间件生态成熟度是绕不开的优势。连接器的种类、监控指标的完善程度、社区问题的沉淀数量、流处理框架的原生支持Kafka都是最完整的。出了问题搜索引擎一搜基本都是可复现的答案团队排障效率高得多。Pulsar的生态相对年轻。核心的系统本身已经在很多场景被验证过但周边的工具链、运维插件、第三方平台集成还在追赶中。如果你所在的团队本来就没多少人做中间件运维选Pulsar之前一定要先做一次小规模试点确认团队能处理BookKeeper层面的问题再扩大到核心业务。6.2 团队技能迁移成本与排障手感的建立从Kafka迁到Pulsar最大的隐性成本不是代码改造而是团队排障手感的重建。Kafka的排障思路是线性的消费没跟上先看consumer group的lag和Rebalance日志再看broker磁盘IO和页缓存问题定位路径很清晰。Pulsar的排障分散在broker和BookKeeper两层出了问题要判断是broker路由问题还是存储层问题还要理解segments、ledgers的组织方式排障曲线明显更陡。举个例子Pulsar消费lag上涨可能是broker端负载问题也可能是BookKeeper的journal写入变慢还可能是客户端的receiverQueueSize设置太小。这三个层面的排查链路完全不同。如果不是团队成员对BookKeeper的内部机制有信心建议在正式迁移前安排一段学习期做一些故障演练比如kill节点、断网、异常重启把团队的手感练出来再上生产。6.3 混合使用与渐进迁移的推荐路径我个人最推荐的落地方式是渐进式迁移而不是一次性全量切换。具体的路径可以从以下几个角度展开新业务直接选择Pulsar利用它的多租户能力做隔离边跑边积累运维经验。老业务继续留在Kafka不要冒险动核心链路。中间的实时数据管道可以用双写策略同一份数据同时写入Kafka和Pulsar等Pulsar侧跑稳之后再逐步摘除Kafka。对于依赖Kafka API的现有客户端也可以用Pulsar的Kafka协议兼容层来做无缝切换客户端代码不用改只需要换一下连接地址。我们实践下来这种方式对现有业务影响最小切换过程是可逆的万一遇到坑还能迅速回滚。另外团队的资源配置也要考虑进去。Pulsar的存储计算分离意味着需要同时运行broker和BookKeeper两组角色集群节点数肯定比同规模的Kafka更多机器成本会上升。如果你的业务规模不大消息量常年稳定在每天几亿条以内用Kafka反而更省心如果消息量真的到了千亿级万亿级而且经常出现突发流量需要弹性伸缩那Pulsar的架构优势才能真正转化为你的成本收益。最后说一点个人体会。中间件选型这件事从来没有绝对正确的答案只有当前阶段下最适合的方案。我见过Kafka集群跑到几千分区时运维团队欲哭无泪也见过Pulsar集群因为BookKeeper节点故障排查三天找不到根因。架构的先进性不能替代团队的掌控力再优秀的系统也需要有人真正理解它才能发挥价值。所以我的建议是先想清楚你的场景要解决的到底是什么问题再用一周时间做一次小规模压测和故障演练让数据帮你做决定而不是让供应商或者博客替你拍板。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询