
1. 从一次集群扩容翻车说起节点、分区、副本到底怎么定很多人学Kafka前几课把生产者消费者跑通之后就觉得这玩意儿已经会了。结果一上生产环境要么是磁盘写满、要么是消费延迟飙升、要么是某个Broker挂了整个集群抖动。我见过一个挺典型的场景一个团队用三台机器搭了个Kafka集群所有Topic都用了默认的单副本分区数随手设了个3。上线三个月数据量涨上来某天一台机器磁盘故障直接导致部分Topic不可用业务方电话打爆。事后复盘发现问题根本不在磁盘而在于一开始节点数、分区数、副本数这三个参数就没认真算过。这三个参数是Kafka集群规划的地基。节点数决定了你的横向扩展能力和容灾下限分区数决定了Topic的并行度和吞吐上限副本数决定了数据的安全等级和写入成本。它们之间不是孤立的而是相互制约的副本数不能超过节点数否则副本无处安放分区数乘以副本数决定了整个集群需要承载的副本总量而副本总量又直接关系到磁盘、网络和Controller的元数据管理压力。这篇内容我打算把这三个参数的设置逻辑掰开揉碎讲清楚然后带一套完整的压力测试方法。适合已经跑通过Kafka基础Demo、准备把Kafka往生产环境推的开发者也适合正在做集群规划、被容量评估搞得头大的运维同学。我不会只告诉你设成多少而是把为什么这么设和设错了会怎样一起讲明白。2. 节点数不是越多越好Broker数量背后的容量账2.1 先搞清楚一个节点到底能扛多少规划节点数的第一步不是拍脑袋决定上几台机器而是先算单节点的承载能力。Kafka的瓶颈通常出现在三个地方磁盘容量、磁盘IO吞吐、网络带宽。这三个里面哪个先到顶哪个就是你的实际瓶颈。磁盘容量的计算相对直接。假设你的业务每天产生100GB原始数据副本数为3数据保留7天那么需要的总存储量是100GB/天 × 3副本 × 7天 2100GB再加上Kafka本身的日志段索引文件、以及一些预留空间建议留20%余量实际需要约2520GB。如果单节点挂载4TB数据盘理论上一个节点就够存。但这里有个关键点Kafka的存储是分散在各个Broker上的分区才是存储的基本单位数据不会自动均衡到所有节点需要靠分区分配策略来保证。磁盘IO吞吐往往比容量更早成为瓶颈。机械硬盘的顺序写吞吐大约在100-200MB/sSSD能到500MB/s以上。Kafka是顺序追加写为主的系统对磁盘的随机读要求不高但对写入吞吐和刷盘延迟敏感。如果你的业务峰值写入是200MB/s用机械盘的话至少需要2-3个节点来分摊写入压力。网络带宽是最容易被忽略的瓶颈。Kafka的写入和副本同步都要走网络。一个副本同步意味着每条消息要额外传输一次副本数减一倍的网络开销。假设峰值写入200MB/s副本数3那么集群内部的副本同步流量就是200×2400MB/s再加上消费者拉取的流量总网络吞吐可能到600MB/s以上。千兆网卡的理论上限是125MB/s万兆网卡是1250MB/s。所以如果你的峰值写入较高千兆网络会直接成为瓶颈。2.2 节点数的下限和推荐值从可用性角度Kafka集群的节点数有一个硬性下限至少3个。原因在于Kafka的Controller选举和分区Leader选举都依赖多数派机制。虽然Kafka的ISRIn-Sync Replica模型不像ZooKeeper那样严格要求多数派但当副本数设为3时如果只有2个节点副本根本无法均匀分布一台机器挂了就可能出现分区不可用。我一般给的建议是场景节点数建议说明开发测试1-3单节点可跑通功能但不具备容灾能力小规模生产3-5能容忍1个节点故障适合日数据量百GB级中等规模5-10容忍1-2个节点故障适合日数据量TB级大规模10需要配合机架感知和分区重分配策略节点数也不是越多越好。每增加一个节点Controller需要管理的元数据就多一份分区Leader选举的协调成本也会上升。更重要的是节点多了之后如果分区分配不均匀容易出现有的节点忙死、有的节点闲死的情况。我见过一个20节点的集群因为Topic创建时没有指定分区分配策略导致80%的分区Leader集中在5个节点上另外15个节点基本在打酱油。2.3 机架感知节点规划里最容易被跳过的一步如果你有多个机架或者多个可用区节点规划时必须考虑机架感知。Kafka允许你给每个Broker配置一个broker.rack参数标识它所在的机架。配置之后Kafka在分配副本时会尽量把同一个分区的副本分散到不同机架。这个配置的价值在于当某个机架整体断电或网络隔离时只要其他机架还有副本存活分区就仍然可用。如果不配机架感知三个副本可能全落在同一个机架上那这个机架一挂数据就全没了。配置方式很简单在server.properties里加上broker.rackrack-1不同机架的Broker填不同的值。这个参数看起来不起眼但在真实故障场景下能救命。我个人的经验是只要你的集群跨了机架或可用区这个参数就必须配没有例外。3. 分区数并行度的天花板也是管理的成本线3.1 分区数决定了什么分区是Kafka并行处理的基本单位。一个Topic的分区数直接决定了三件事第一生产者的最大并行度。生产者可以同时向多个分区写入分区越多能同时写入的线程就越多。但注意如果生产者没有指定分区键消息会轮询写入各个分区如果指定了分区键同一个键的消息永远进同一个分区。第二消费者的最大并行度。在消费者组模式下一个分区只能被组内一个消费者消费。所以消费者实例数超过分区数时多出来的消费者会处于空闲状态。这就是为什么分区数要提前规划好——后期增加分区虽然可以但会打破按key分区的语义一致性。第三吞吐量的上限。单个分区的写入吞吐是有限的受限于磁盘顺序写速度和网络带宽。实测下来单个分区在SSD上的写入吞吐大约在50-100MB/s。如果你的目标吞吐是500MB/s那至少需要5-10个分区。3.2 分区数的计算公式与实操建议分区数的估算没有一个万能公式但可以用一个经验方法分区数 max(目标吞吐 / 单分区吞吐, 目标消费者并行数)举个例子目标写入吞吐300MB/s单分区实测吞吐80MB/s那么至少需要4个分区。同时你计划最多用8个消费者实例来消费那么分区数至少要是8。取两者最大值分区数设为8。但这只是下限。实际设置时还要考虑预留增长空间业务量会涨分区数建议在计算值基础上乘以1.5到2倍。但也不能太夸张分区太多会增加元数据管理成本和文件句柄数。分区数最好是消费者实例数的整数倍这样每个消费者分到的分区数一致负载均衡。比如计划用6个消费者分区数设12或18比设10更合理。单集群分区总数有上限Kafka单个Broker能承载的分区数受限于内存和文件句柄。一般来说每个分区大约占用几十KB的元数据内存加上每个分区对应的日志段文件句柄。经验值是单Broker承载2000-4000个分区比较稳妥超过之后Controller的元数据同步和Leader选举会变慢。我一般给的建议是小规模业务分区数设6-12中等规模设12-48大规模设48-128。超过128个分区的Topic要慎重除非你确实有极高的并行度需求。3.3 分区数设多了会怎样分区数不是越多越好设多了有几个实实在在的坑坑一文件句柄耗尽。每个分区在Broker上对应一组日志段文件包括.log、.index、.timeindex。一个分区至少3个文件句柄1000个分区就是3000个句柄。Linux默认的单进程文件句柄限制是1024不调的话直接报错。需要修改ulimit -n和Kafka的num.file.handles相关配置。坑二Leader选举变慢。Controller在Broker故障时需要对受影响的分区逐个进行Leader选举。分区越多选举耗时越长故障恢复期间不可用的时间窗口就越大。坑三端到端延迟增加。生产者写入多个分区时需要等待所有分区的确认如果acksall。分区越多最慢的那个分区决定整体延迟长尾效应更明显。坑四消费者Rebalance变慢。消费者组Rebalance时需要重新分配所有分区。分区越多协调器计算分配方案和同步状态的时间就越长。3.4 分区数能不能改可以改但只能增不能减。增加分区用kafka-topics.sh的--alter --partitions参数kafka-topics.sh --bootstrap-server broker1:9092 \ --alter --topic my-topic --partitions 24但增加分区有两个副作用一是如果生产者按key分区增加分区后同一个key可能映射到不同的分区导致顺序性被破坏二是已经写入的数据不会重新分布新分区一开始是空的需要等新数据写入才会均衡。所以我的建议是分区数在创建Topic时就规划好尽量避免后期修改。如果确实要改最好在业务低峰期操作并且确认业务能接受key映射变化带来的影响。4. 副本数数据安全的代价与收益4.1 副本数的本质是什么副本数replication factor决定了每个分区有多少份数据拷贝。副本数设为3意味着每个分区的数据会在3个不同的Broker上各存一份。其中一个是Leader负责处理读写请求另外两个是Follower负责从Leader同步数据。副本数的核心价值是容灾。当Leader所在的Broker故障时Kafka会从ISRIn-Sync Replica同步副本集合中选出一个新的Leader保证服务不中断。副本数越多能容忍的Broker故障数就越多。但副本不是免费的。每增加一个副本就多一份磁盘存储、多一份网络同步流量、多一份写入延迟因为acksall时要等所有ISR副本确认。所以副本数的选择本质上是可用性和成本之间的权衡。4.2 副本数设多少合适我的建议很直接场景副本数说明开发测试1不要求容灾省资源一般生产2容忍1个Broker故障成本适中核心业务3容忍1-2个Broker故障行业标准极高要求3配合机架感知但成本急剧上升副本数设为3是大多数生产环境的标配。它能在容忍一个Broker故障的同时仍然保持ISR中有足够的副本参与选举。如果副本数设为2一台机器挂了之后ISR可能只剩1个副本此时如果这个副本也出问题分区就不可用了。副本数不能超过Broker节点数。如果你只有2个Broker副本数最多设2。设3的话创建Topic时会报错因为Kafka无法把3个副本分配到2个节点上同一个Broker上不会放同一个分区的多个副本。4.3 min.insync.replicas比副本数更容易被忽略的参数副本数设了3不代表写入就一定安全。这里有一个关键参数min.insync.replicas它定义了写入时至少需要多少个副本确认。这个参数可以设置在Broker级别也可以设置在Topic级别。举个例子副本数3min.insync.replicas设为2生产者acksall。此时如果ISR中只剩1个副本另外两个都挂了或同步滞后写入会被拒绝抛出NotEnoughReplicasException。这看起来是坏事实际上是保护机制——它防止了数据只写入一个副本就返回成功避免那个副本再挂导致数据丢失。如果min.insync.replicas设为1那么即使ISR只剩Leader自己写入也能成功。这时候如果Leader挂了数据就丢了。我的建议是min.insync.replicas设为副本数减一。副本数3就设2副本数2就设1。这样在容忍一个副本故障的同时仍然保证至少有两个副本持有数据。4.4 副本分配策略与机架感知的配合Kafka在创建Topic时会自动分配副本到各个Broker。默认策略是尽量均匀分布同时保证同一个分区的副本不在同一个Broker上。如果配置了机架感知还会尽量把副本分散到不同机架。你可以用以下命令查看分区的副本分配情况kafka-topics.sh --bootstrap-server broker1:9092 \ --describe --topic my-topic输出会显示每个分区的Leader、Replicas列表和ISR列表。如果发现某个Broker上集中了过多Leader可以用kafka-reassign-partitions.sh工具手动重分配。手动重分配的步骤稍微繁琐一些需要先写一个重分配计划文件然后执行。这里不展开但记住一个原则Leader应该尽量均匀分布在所有Broker上避免热点。5. 压力测试用数据说话别靠猜5.1 为什么必须做压力测试参数规划得再好也是基于估算。真实环境中的磁盘性能、网络延迟、消息大小分布、生产者消费者行为都会影响实际表现。压力测试的目的就是把这些变量跑出来验证你的参数设置是否合理找出真正的瓶颈在哪里。Kafka自带了两套压测工具kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh。它们能模拟生产者和消费者的行为输出吞吐量、延迟等关键指标。5.2 生产者压测怎么跑、看什么生产者压测的基本命令kafka-producer-perf-test.sh \ --topic perf-test \ --num-records 1000000 \ --record-size 1024 \ --throughput -1 \ --producer-props \ bootstrap.serversbroker1:9092,broker2:9092,broker3:9092 \ acksall \ linger.ms10 \ batch.size32768 \ compression.typelz4参数解释--num-records发送的消息总数100万条。--record-size每条消息大小1024字节即1KB。--throughput -1不限速全力发送。acksall等待所有ISR副本确认模拟最严格的写入场景。linger.ms10生产者等待10ms再发送让消息攒成更大的批次。batch.size32768批次大小32KB。compression.typelz4使用LZ4压缩减少网络传输量。输出会显示类似这样的结果records sent, 125000.0 records/sec (122.07 MB/sec), avg latency: 45.23 ms, 95th: 89.12 ms, 99th: 156.78 ms重点看三个指标吞吐量records/sec和MB/sec、平均延迟、P99延迟。吞吐量反映集群的处理能力P99延迟反映长尾情况。如果P99延迟远高于平均值说明存在明显的抖动可能是GC、磁盘刷盘或网络拥塞导致的。5.3 消费者压测验证消费能力消费者压测命令kafka-consumer-perf-test.sh \ --bootstrap-server broker1:9092,broker2:9092,broker3:9092 \ --topic perf-test \ --messages 1000000 \ --threads 4 \ --group perf-consumer-group--threads 4表示用4个消费者线程并行消费。输出会显示消费的总数据量、消费速率和耗时。消费者压测的关键是看消费速率能否跟上生产速率。如果生产端能跑到120MB/s消费端只能跑到80MB/s那消费延迟就会持续累积。这时候要么增加消费者实例但受限于分区数要么优化消费逻辑。5.4 压测中的常见陷阱压测看起来简单但有几个坑很容易踩坑一压测数据被保留策略清掉。如果Topic的retention.ms设得很短压测写入的数据可能还没消费就被删了。压测前把保留时间调长或者用专门的压测Topic。坑二压测客户端和Broker在同一台机器。这样压测的是本地回环网络测不出真实网络带宽瓶颈。压测客户端应该放在独立的机器上通过网络访问Broker。坑三只测单Topic。真实环境中多个Topic共享集群资源单Topic压测的结果会偏乐观。有条件的话模拟多Topic并发写入的场景。坑四忽略JVM GC影响。Kafka Broker是JVM应用GC停顿会直接影响延迟。压测时监控Broker的GC日志如果Full GC频繁需要调整堆大小和GC参数。坑五压测时间太短。跑几十秒就下结论是不靠谱的。建议至少跑10-15分钟让系统进入稳定状态观察长期趋势。6. 一套可复现的集群规划与压测流程6.1 从业务指标反推参数假设一个具体的业务场景日增数据量500GB峰值写入吞吐200MB/s消息平均大小1KB要求容忍1个Broker故障数据保留3天。第一步算存储。500GB/天 × 3副本 × 3天 4500GB加20%余量约5400GB。如果单节点挂4TB盘至少需要2个节点存数据但考虑容灾和负载均衡建议3-4个节点。第二步算分区数。峰值200MB/s单分区按80MB/s算至少3个分区。考虑消费者并行度和增长空间设12个分区比较稳妥。第三步定副本数。要求容忍1个Broker故障副本数至少2。但为了更高的安全性和ISR弹性建议设3。第四步验证节点数是否够。12分区 × 3副本 36个副本分布在3个节点上每个节点12个副本。单节点承载12个分区完全没问题。磁盘方面5400GB / 3节点 1800GB/节点4TB盘够用。6.2 创建Topic并验证分配kafka-topics.sh --bootstrap-server broker1:9092 \ --create --topic business-topic \ --partitions 12 \ --replication-factor 3 \ --config min.insync.replicas2 \ --config retention.ms259200000创建后用--describe检查副本分配是否均匀Leader是否分散在不同Broker上。6.3 分阶段压测先跑一轮基准压测用默认参数记录吞吐和延迟。然后逐步调整参数比如增大batch.size、开启压缩、调整linger.ms观察每次调整对吞吐和延迟的影响。最后用接近生产环境的参数组合跑一轮长时间压测确认稳定性。压测过程中要同时监控Broker端的指标磁盘使用率、网络吞吐、CPU、GC频率、ISR收缩次数。这些指标能帮你定位瓶颈到底在哪个环节。6.4 压测后的参数微调压测结果如果显示吞吐不达标优先检查分区数是否够、批次大小是否合理、压缩是否开启、磁盘是否成为瓶颈。如果延迟偏高检查GC、acks设置、linger.ms是否过大。如果ISR频繁收缩说明副本同步跟不上可能是网络带宽不足或磁盘IO瓶颈。这时候要么增加Broker分摊压力要么降低写入速率。7. 几个我踩过的坑和对应的解法7.1 副本数改了但ISR一直不恢复有一次帮一个团队排查问题他们把副本数从2改成了3但新加的副本一直不在ISR里。原因是新副本所在的Broker磁盘IO太慢同步速度跟不上Leader的写入速度一直被踢出ISR。解法是先把写入速率降下来等新副本追上进度后再恢复。或者用kafka-reassign-partitions.sh把副本迁移到性能更好的节点上。7.2 分区数增加后消费顺序乱了一个业务方反馈增加分区后同一个用户的消息偶尔会乱序。原因是他们用用户ID做key增加分区后key的哈希映射变了同一个用户的消息可能落到不同分区。解法是如果业务强依赖顺序增加分区后需要暂停消费等旧数据消费完再切换。或者改用其他方式保证顺序比如在消息里带序列号消费端做重排序。7.3 压测跑得很漂亮但生产环境拉胯压测时用1KB的小消息跑出了很高的吞吐但生产环境消息平均10KB吞吐直接掉了一半。原因是大消息的批次效率低网络传输和磁盘写入的开销都更大。教训是压测的消息大小要贴近真实业务最好用真实的消息样本做压测。7.4 节点数够了但磁盘先满了一个集群规划时算了容量够用但运行两个月后磁盘告警。排查发现是日志段文件没有及时清理因为retention.ms设的是7天但log.retention.check.interval.ms默认是5分钟清理有延迟。更关键的是他们没考虑消息写入速率的波动某几天业务高峰写入量翻倍直接把磁盘撑满了。解法是设置磁盘使用率告警并且把保留策略改成基于磁盘使用率的retention.bytes双保险。8. 关于参数规划我的几条个人原则做了这么多集群规划和压测我总结了几条自己一直坚持的原则分享出来供参考。第一副本数永远不要设1。除非是纯测试环境否则单副本就是在赌运气。磁盘故障、机器宕机、网络分区任何一个发生都会导致数据丢失。副本数3的成本比数据丢失的代价低得多。第二分区数宁多勿少但不要超过单Broker 2000个。分区数少了后期扩展麻烦多了管理成本高。2000个是我个人经验中比较稳妥的单Broker上限超过之后Controller的元数据同步和Leader选举会明显变慢。第三压测一定要用真实消息样本。消息大小、压缩率、key的分布这些都会影响实际表现。用随机生成的数据压测结果只能参考不能作为决策依据。第四监控比参数更重要。参数设得再好没有监控就是盲飞。至少监控这几个指标Broker的磁盘使用率、网络吞吐、ISR收缩次数、UnderReplicatedPartitions数量、生产者和消费者的延迟。这些指标能让你在问题爆发前就发现苗头。第五集群规划要留余量。不管是磁盘、网络还是CPU都不要跑到100%。留30%左右的余量应对业务增长和突发流量。我见过太多集群在业务翻倍时直接崩掉就是因为当初规划时卡得太死。最后说一个我自己的习惯每次调整集群参数或扩容之后都会重新跑一轮压测确认新的配置下性能符合预期。参数是死的业务是活的只有持续验证才能保证集群始终处于健康状态。