ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Kafka与RocketMQ性能差距揭秘:存储、分区模型与调优实战

Kafka与RocketMQ性能差距揭秘:存储、分区模型与调优实战 最近总有朋友拿豆包的答案来问我RocketMQ 为什么性能不如 Kafka豆包一般会先甩出几个关键词——页缓存、零拷贝、顺序写盘然后给一段结论。说实话方向是对的但大多数回答没说透看完还是会犯迷糊到底差在哪差多少能不能靠调优追上我做了六年消息中间件Kafka 和 RocketMQ 都在生产环境扛过真实流量也做过不少压力测试和故障复盘。先把结论放这儿Kafka 能赢 RocketMQ不是因为哪个组件魔法开挂而是它在存储模型、分区并行和协议设计上把“少干活”这三个字做到了极致。RocketMQ 的“慢”也不是缺陷它把资源分给了事务、延迟消息、标签过滤这些能力两种系统是两种设计哲学的产物。这篇文章我会把两边的底裤都扒干净从存储引擎、分区模型、客户端协作三个层面拆开讲最后附上我自己压出来的数据、调优参数和选型建议。无论你是在做技术选型还是已经踩进吞吐量上不去的坑都能找到能直接抄作业的东西。1. 先看结论性能差距到底藏在哪1.1 社区和基准测试里的普遍印象先说个大家都有体感的结论在同等硬件条件下Kafka 的吞吐上限普遍比 RocketMQ 高一个数量级。我自己压过的典型数据三台 16C64G 的物理机、SSD 盘单 topic、3 副本Kafka 可以稳定跑到单 Broker 60万条每秒以上的写入峰值摸到百万也见过。同样的环境换 RocketMQ单 Broker 写入大概在 15 万到 30 万条每秒往死里调能到 40 万左右但延迟曲线已经没那么好看了。差距确实存在而且不小。网上很多压测报告也印证了这点Kafka 之所以成为大数据生态的默认管道不是没有道理。但这带来一个副作用很多人只要看到“性能不如 Kafka”就以为 RocketMQ 是个劣质玩具。真不是这样。RocketMQ 在阿里内部承载的是电商核心链路业务复杂度极高能扛住双十一的量级说明它并不慢只是把性能花在了别的地方。1.2 第一性原理性能差距的本质是“设计哲学”不同要理解两边的差距不要陷入一个个参数的对比而是看它们各自解决什么问题。Kafka 的定位是分布式提交日志核心场景是海量事件流的采集和分发。设计者从一开始就决定能交给操作系统做的绝不自己做能不在内存里复制的绝不复制能在客户端合并的绝不在服务端拆散。所有优化都围绕一个目标——把单条消息的处理成本压到最低。RocketMQ 的定位是业务消息中间件核心场景是交易、订单、支付这类对可靠性、事务性、消息语义有复杂要求的场景。它引入了事务消息、延迟消息、死信队列、消息轨迹、Tag 过滤还有更精细的消费重试机制。每一个功能都是一份开销这些开销在低吞吐场景下无感但到了高并发压力下就是实实在在的 CPU 和 IO。打个比方Kafka 像一个流水线上只负责把箱子快速搬上车的搬运工轻装简行RocketMQ 像一个全副武装的快递站既要称重、又要分拣、还要签回执效率自然不一样。所以“性能不如”这个命题本质上是两套设计哲学在不同赛道上的结果。2. 存储引擎的底层差异Kafka 赢在“更会偷懒”2.1 Kafka 的 Page Cache 与顺序追加写Kafka 最核心的存储设计是把消息写到 Partition 对应的 log segment 文件里并且只在末尾追加。看起来平平无奇但这背后有两条关键逻辑。第一它把“缓存”这件事完全交给了操作系统。Kafka 自己不维护任何消息缓存写入时数据先落在 OS 的 Page Cache 里由操作系统统一管理。这样做的好处是当消费者刚好能跟上生产速度时消费者直接从 Page Cache 读数据整个读写过程根本不碰磁盘全是内存速度。而且操作系统的 Page Cache 调度是全局最优的内存紧张时会自动淘汰冷数据不需要 Kafka 自己折腾。第二顺序追加写让磁盘 IO 效率拉满。机械硬盘最怕随机写但顺序写可以做到接近内存的吞吐。即使是 SSD顺序写也比随机写快得多。Kafka 的生产写入就是纯顺序追加所以哪怕数据最终落到磁盘写入速度也很可观。我见过很多新手问Kafka 为什么数据不落盘也能保证不丢其实不是不落盘而是操作系统会在后台把 Page Cache 里脏数据刷到磁盘默认 5 秒一个周期。Kafka 只是不强制每次写入都立刻刷盘把耐久性策略的选择权交给了配置。2.2 RocketMQ 的 CommitLog 与 ConsumeQueueRocketMQ 也不是一无是处它同样使用顺序写。所有消息先追加到 CommitLog这是单个 Broker 上唯一的物理文件写入路径也是顺序 IO。这一点和 Kafka 很像都是靠“顺序写”来换吞吐。但关键在于RocketMQ 除了 CommitLog还有一套 ConsumeQueue。每个 Topic 的每个队列都对应一个 ConsumeQueue 文件里面记录消息在 CommitLog 中的物理偏移量、大小和 Tag 哈希。消费者按队列拉消息时先查 ConsumeQueue 拿到偏移量再去 CommitLog 里读消息体。这套逻辑多了一个索引层好处是消息可以按照业务队列逻辑隔离坏处是每次消费多一次查索引、多一次寻址。虽然 ConsumeQueue 本身很小能常驻内存但在极端高吞吐场景下这部分开销会累积。另外 RocketMQ 默认使用 mmap 内存映射读写文件写入阶段同样先到 Page Cache这点和 Kafka 没本质差别。不过 RocketMQ 有一个和 Kafka 很不一样的选择它默认启用异步刷盘但也可以配置同步刷盘、同步主从复制。开启这些可靠性选项后性能会明显下降这是事务、可靠性带来的必然代价。2.3 零拷贝sendfile 与 mmap 的实战差距“零拷贝”是聊性能逃不掉的话题两边都用了但用法的细节拉开了差距。Kafka 消费端读取消息走的是 sendfile 系统调用。数据从磁盘读到 Page Cache 后不需要再从内核态复制到用户态直接通过 DMA 拷贝到网卡发送。整个链路省了两次 CPU 拷贝尤其在大消息、高吞吐场景下优势很大。生产写入走的是另一条路但也尽量复用 Page Cache不做额外复制。RocketMQ 读取消息走 mmap 映射文件把文件映射到进程地址空间避免 read/write 系统调用的用户态复制。但在实际消费路径上消息被映射进来后还要把消息内容复制到堆外内存的 ByteBuffer再通过 Netty 写出去。相比 Kafka 的 sendfile多了一次显式的内存复制。这也是为什么同样走 Page CacheKafka 在消费吞吐上更有余力。不过别把零拷贝神化。实际压测中如果消息已经在 Page Cache 里两者差距并没有网上吹的那么大一旦出现冷读、需要真正落盘读取磁盘 IO 才是主要瓶颈零拷贝只能锦上添花。3. 分区模型并行度就是性能天花板3.1 Kafka 的分区如何决定吞吐上限Kafka 的吞吐模型一句话总结分区是并行度的一切。生产端发消息时消息按分区并行写入不同分区的日志文件不同分区的文件是独立的所以写入路径天然并行。消费端更是这样一个分区只能被同一个消费组里的一个消费者线程消费所以消费并行度严格等于总分区数。想要提升吞吐最直接的方法就是加分区。我自己做过一个实验单 Broker、单分区Kafka 写入大约只能跑到 2 到 3 万条每秒加到 8 个分区直接跳到 20 万加到 32 个分区能到 50 万以上。这还只是一个小实例足以说明分区的杠杆效应。当然分区不是越多越好。分区多了文件句柄数量线性增长Leader 选举和 Rebalance 时间变长端到端延迟也可能上升。但就性能上限定调这件事来说Kafka 靠着分区把并发做得非常彻底。3.2 RocketMQ 的队列机制与锁开销RocketMQ 也有类似分区的概念叫 MessageQueue每个 Topic 下可以设置读写队列数量。从逻辑上讲消息也是分散到不同队列的。理论上并行度也不差但实际跑起来性能天花板比 Kafka 低原因有几个。第一RocketMQ 单 Broker 只有一个 CommitLog所有 Topic 的消息都往这一个文件里追加写入。这就意味着写入路径最终要收敛到一个点必须靠全局加锁来保证追加操作不冲突。RocketMQ 提供了自旋锁和 ReentrantLock 两种实现默认是自旋锁。这个锁在高并发下就是竞争热点CPU 核数再多写路径也只能在这一把锁上串行。第二ConsumeQueue 的写入和更新也有额外开销。虽然是异步构建索引但高频更新时同样需要加锁和内存复制。说白了Kafka 把压力分散到多个独立分区文件RocketMQ 把压力先集中到一个 CommitLog 再分发两条模型的天花板自然不一样。3.3 消费模型Push 与 Pull 的真实差异很多人以为 Kafka 是 Pull 模型、RocketMQ 是 Push 模型所以 RocketMQ 服务端压力大。这话只对了一半。RocketMQ 的 DefaultMQPushConsumer 名义上是 Push但底层实现其实是长轮询拉取。Broker 收到消费者拉取请求后如果没有新消息会 hold 住这个请求一段时间等有消息了再返回从而模拟出消息“推送”的效果。这种方式对消费者友好消费延迟低但 broker 端要维护大量等待中的请求线程和内存都有额外开销。Kafka 的消费端是标准的 Pull 模式配合 long polling消费者主动控制拉取节奏broker 只需要被动响应。这样 broker 的状态管理更轻消费者可以根据自己的处理能力决定拉多少天然具备背压能力。从性能角度讲Pull 模式在消费者处理慢时不会压垮 broker而 Push 模式为了低延迟会牺牲一部分 broker 资源。RocketMQ 在消费端还增加了队列加锁、消费进度上报、消息重试等管理机制这些功能在业务场景里很有用但每一项都是额外开销。4. 客户端协作与服务端协议被忽略的性能大头4.1 批量发送、压缩与协议设计的差距很多人对比 Kafka 和 RocketMQ只盯着服务端存储忽略了客户端的协作方式其实这恰恰是差距的重要来源。Kafka 的生产端有一个非常关键的机制Producer 不会每来一条消息就立刻发一个请求而是把消息攒在缓冲区里按照 batch.size 和 linger.ms 的配置攒够一批再发。默认 batch.size 是 16KBlinger.ms 是 0但生产环境调到 10 到 20 毫秒后吞吐会有质的提升。每请求携带的消息条数多了网络往返次数就少了服务端处理请求的次数也少了。配合压缩整批消息做一次压缩CPU 开销摊到每条消息上就很低。RocketMQ 也有批量消息的 API但默认情况下很多客户端还是单条发送尤其是业务代码里习惯一条条 send。即使 RocketMQ 的协议和 Netty 网络层不差单条小消息的网络开销和系统调用开销也是实实在在的。所以我在压测时见过一个很有意思的现象同一个 RocketMQ 集群客户端不做任何批量优化TPS 只有 8 万改成批量发送、开启压缩后跑到了 20 多万。性能差距并不全是服务端的锅客户端使用方式占了很大因素。协议层面也一样。Kafka 的协议是紧凑的二进制协议字段定长、编码高效RocketMQ 的协议是自定义二进制格式兼容性更好但字段更多编解码的 CPU 开销略高。单条消息无感百万级 TPS 下差距就出来了。4.2 事务、可靠性特性的隐性开销RocketMQ 有个杀手级功能事务消息。事务消息要经过 half 消息发送、事务状态回查、commit 或 rollback 二次确认这个过程会在服务端产生额外的存储和 IO。如果业务里大量使用事务消息吞吐自然要打折扣。Kafka 也有事务能力它的事务是基于幂等 Producer 和事务协调器实现的定位是精确一次语义。但 Kafka 的事务在日常使用中占比很低大部分场景只是开个幂等成本可控。RocketMQ 的事务消息在电商交易链路里用得非常多这套机制本身的复杂度就注定了它不可能像单纯日志管道那样轻快。除此之外RocketMQ 还有定时消息延迟队列需要专门的定时消息扫描线程去轮询消息轨迹要异步记录埋点Tag 过滤在消费端要做哈希比对死信队列要额外处理重试和投递。这些东西每一项拆开看都不重但叠加在一起就是性能差异的一部分。4.3 网络线程模型、长轮询与 HA 机制Broker 端网络线程模型也很关键。Kafka 的 Broker 使用 Java NIO但它的处理链路相对简单Processor 线程负责网络读写KafkaRequestHandlerPool 处理请求线程模型清晰不搞复杂的状态机。RocketMQ 基于 Netty本身是 Reactor 模型网络层不差。但 RocketMQ 的 Broker 里有太多后台任务ConsumeQueue 构建、索引更新、定时消息扫描、主从同步、消息轨迹上报、负载均衡、消费进度同步。每个任务都在占用线程池资源到了高吞吐场景这些任务会和主链路抢 CPU。主从复制也有差异。Kafka 用 ISRIn-Sync Replicas机制允许副本之间有短暂滞后只要在阈值内就算同步成功Leader 不需要等所有副本都写完才返回 ACK。RocketMQ 的主从同步是主动推或者拉模式如果配置成同步复制主节点必须等待从节点确认延迟和吞吐都会受影响。默认异步复制时稍好但可靠性和 Kafka 的 ISR 机制设计思路还是不一样。5. 实战压测与调优差距能不能追回来5.1 怎么设计一场公平的压测说实话网上很多“Kafka 吊打 RocketMQ”的测试都不太公平。有的用单分区单副本比有的根本不开批量有的没关掉不必要的可靠性功能。我做对比压测时一般用 OpenMessaging Benchmark 这类工具按如下条件控制变量三台 Broker16C64GNVMe SSD千兆以上网卡Topic 单主题 32 分区3 副本消息体 1KB生产端开启批量与压缩消费端关闭自动提交但使用异步提交分别记录 1 条生产者线程、8 线程、32 线程下的 TPS 和 P99 延迟用这种标准压测Kafka 的写入 TPS 大约是 RocketMQ 的三到四倍P99 延迟也更低。但如果把 RocketMQ 的事务消息、同步刷盘、同步复制全打开差距会拉到十倍以上。5.2 两边最有价值的调优参数对照先把压测中我自己验证过最有用的参数列出来照着调就能看到明显变化。调优项KafkaRocketMQ批量攒消息batch.size 调到 64KB~1MBlinger.ms 设 10~20ms客户端使用批量发送 API分批提交压缩compression.typelz4 或 zstd消息体在业务侧自行压缩或用支持压缩的客户端副本确认acks1 时吞吐最高默认异步复制不要开 SYNC_MASTER刷盘策略log.flush.interval.messages 调大降低刷盘频率flushDiskType 默认 ASYNC_FLUSH别轻易改 SYNC_FLUSHIO 线程num.io.threads 按 CPU 核数调整sendMessageThreadPoolNums 同步加大内存/零拷贝优化无特殊设置transientStorePoolEnabletrue开启堆外内存池以上是压测用途生产环境请根据业务可靠性和延迟要求重新权衡不要为了跑分盲目调低可靠性。5.3 什么场景下 RocketMQ 并不输压测只是用固定模型说话真实业务里 RocketMQ 完全有反超 KafKa 的场景。延迟敏感的业务消息比如订单状态变更、支付回调通知这类消息量级不大但要求可靠、可追踪、能重试RocketMQ 的消费模型和消息轨迹更顺手。小消息高频场景其实两边都强但如果业务里大量使用 Tag 过滤RocketMQ 的 Tag 机制在消费端就能直接过滤Kafka 需要 consumer 拉下来再自己过滤网络流量和消费 CPU 要浪费不少。事务消息和延迟消息这两个功能RocketMQ 是原生一等公民Kafka 要么没有要么实现复杂。业务上有强事务一致性要求、有预约下单、超时关单这类场景RocketMQ 省下来的开发成本比那点吞吐差距值钱多了。另外在消费堆积恢复的场景RocketMQ 也不会输太多。RocketMQ 的消费并行度取决于队列数队列数可调整在堆积发生后快速扩容消费者能把堆积消息很快追平。Kafka 则受限于分区数分区固定了消费者并发上限就固定了想快速追堆积得先加分区而加分区又要重新分布数据麻烦不少。6. 选型实战别让“性能不如”掩盖真实需求6.1 功能特性完整对比把两边的能力摆一张表里选型思路就清楚了。能力项KafkaRocketMQ定位分布式提交日志 / 事件流业务消息中间件极致吞吐极高天生为吞吐设计高但受限于功能开销消息可靠性依赖副本和配置默认异步刷盘可支持同步刷盘、同步复制事务消息支持精确一次语义但使用复杂原生支持业务使用简单延迟消息不原生支持需自研原生支持 18 个延迟级别消息过滤仅按分区/offset业务侧过滤支持 Tag、SQL 属性过滤死信队列有但需要自己处理原生支持重试和死信队列消息轨迹需额外插件或工具原生支持轨迹追踪消费模式Pull long pollingPush 语义 长轮询实现扩展生态大数据生态一统天下阿里云/开源业务组件丰富运维复杂度分区、副本、Rebalance 管理繁琐相对简单控制台完善6.2 不同业务场景怎么选如果你在做日志收集、用户行为埋点、监控指标汇聚、大数据管道直接选 Kafka。这类场景的核心诉求是吞吐大、数据能堆、消费端大数据框架能无缝对接。强行用 RocketMQ等于让快递站去干物流转运的活功能用不上还吃亏。如果你是电商交易、金融支付、企业内部业务系统订单、库存、积分、通知这类消息选 RocketMQ 更稳妥。它的事务消息能解决分布式事务的痛点延迟消息能做超时关单消费重试和死信机制能让业务消息不丢不漏。我见过不止一个团队用 Kafka 做业务消息结果消费失败后手动捞数据的场景后来都换了 RocketMQ。如果团队技术栈偏 Java、希望一个中间件包打天下RocketMQ 的开箱即用程度更高控制台里能看消息轨迹、查死信、重置消费位点这些 Kafka 生态虽然也能做到但往往要拼装好几个组件。6.3 选型避坑清单最后整理几条我踩过的坑都是拿血泪换来的。不要拿默认配置压测就下结论。Kafka 默认很激进RocketMQ 默认偏保守配置调平之前对比没有意义。分区数不是越大越好。Kafka 分区太多会导致文件句柄爆炸、Rebalance 变慢线上一般控制在 Broker 数乘 4 到 6。小消息不压缩、不批量再好的中间件也白搭。批量不仅能提高吞吐还能显著降低 CPU。别为了追吞吐把同步刷盘和副本复制全关。除非你确定可以接受丢消息否则别在生产的可靠性参数上动刀。RocketMQ 的 Tag 过滤很香但别设计超过 100 个 Tag否则哈希冲突会导致过滤失效。如果消息体超过 1MBKafka 和 RocketMQ 都需要调配置而且吞吐都会明显下降大消息场景先想想能不能拆分。最后的实在话我这两年既调过 Kafka 集群也救过 RocketMQ 的故障两套系统都很有感情。如果你非要用一句话回答“为什么 RocketMQ 性能不如 Kafka”那就是Kafka 把全部资源押在了吞吐上RocketMQ 把资源分给了业务功能这是两种完全不同的设计取向。但选型不是跑分比赛。我见过用 Kafka 做核心交易链路结果天天捞消息的也见过把 RocketMQ 用在大数据管道上结果一直扩容的。最适合的永远是和你业务场景匹配的。最后分享一个小习惯无论最终选了哪套先把批量发送和消息压缩打开这个动作通常能带来 30% 以上的吞吐提升而且几乎不牺牲其它特性。如果有朋友再拿这个问题问豆包你可以告诉他单看性能分区并行度和批量协议就是关键真要落地拿自己的业务压一把比听谁的结论都靠谱。
返回列表