2. Kafka主题与分区设计

在Kafka中,主题(Topic)是消息的逻辑分类,生产者向主题写入、消费者从主题读取;分区(Partition)则是主题的物理并行单元,是Kafka实现高吞吐、水平扩展和顺序保证的基础。理解主题与分区设计,本质上是在回答三个问题:数据放在哪里、并行度开多大、副本如何保证不丢。本节围绕主题创建、分区策略、副本机制与数据保留策略展开,并完成一个多分区主题的规划与创建任务。

一、主题与分区的基本结构

一个主题可以被拆分为多个分区,每个分区是一个有序、不可变的消息日志(Log)。消息在分区内按偏移量(Offset)递增排列,Kafka只保证分区内有序,不保证主题全局有序。分区可以分布在不同的Broker上,从而实现读写并行。每个分区还有若干副本(Replica),其中一个是Leader,负责处理读写请求;其余为Follower,持续从Leader同步数据。当Leader所在Broker故障时,会从同步副本中选举新的Leader。

因此,主题设计不是简单的“建一个名字”,而是要同时确定:分区数量、副本因子、分区键策略、保留时间或大小,以及清理策略。这些参数一旦确定,后期调整分区数虽然可行,但会改变消息的路由结果,副本因子调整也涉及数据搬迁,所以规划阶段应尽量贴近业务真实吞吐与可靠性要求。

主题 orders-topic 的分区、副本与保留策略 生产者 按 key 计算分区 Topic: orders-topic(分区数=3,副本因子=2) Partition 0 Leader Broker 1 Follower Broker 2 offset: 0 → 1280 Partition 1 Leader Broker 2 Follower Broker 3 offset: 0 → 960 Partition 2 Leader Broker 3 Follower Broker 1 offset: 0 → 1520 消费者组 3 个消费者实例 各消费 1 个分区 保留策略 retention.ms = 604800000 (7 天) cleanup.policy = delete

二、分区策略:消息如何落到具体分区

生产者发送消息时,分区器(Partitioner)决定消息进入哪个分区。常见策略有三类:

  • 指定分区号:生产者在消息中直接指定partition,消息进入该分区,适合需要严格控制顺序或做定向路由的场景。
  • 按Key哈希:若消息带Key且未指定分区,Kafka对Key做哈希并对分区数取模,相同Key总是进入同一分区,从而保证同一业务实体的顺序。例如订单ID作为Key,同一订单的创建、支付、发货事件会进入同一分区。
  • 轮询或粘性分区:无Key时,早期版本采用轮询,新版本默认使用粘性分区(Sticky Partitioner),在批次未满时尽量把消息发往同一分区,减少小批次、提升吞吐,批次切换时再换分区。

分区数决定了消费者组的最大并行度:一个分区在同一消费者组内只能被一个消费者消费。因此,若消费者实例数超过分区数,多出的实例会空闲。规划分区数时,通常取“峰值吞吐 ÷ 单分区可承载吞吐”与“期望消费者并行数”中的较大值,并预留一定增长空间。但分区也不是越多越好:分区过多会增加Broker元数据、文件句柄、Leader选举和再平衡的开销。

三、副本机制:可靠性如何落地

副本因子(replication.factor)表示每个分区保存几份数据。副本因子为1时没有冗余,Broker故障会丢数据;生产环境通常至少为2,核心链路建议为3。副本中只有Leader对外服务,Follower异步拉取。与副本相关的关键参数包括:

  • acks:生产者要求多少个副本确认。acks=0不等待确认,吞吐最高但可能丢;acks=1只等Leader写入;acks=all等待ISR中所有副本确认,可靠性最高。
  • min.insync.replicas:ISR中至少有多少副本在线才允许写入。若设为2、副本因子为3,则允许1个副本故障仍可写入;若在线副本少于2,写入会被拒绝,避免数据只落在少数副本上。
  • unclean.leader.election.enable:是否允许非ISR副本当选Leader。设为true可提高可用性,但可能丢失数据,通常建议保持false。

ISR(In-Sync Replica)是与Leader保持同步的副本集合。Follower若落后超过replica.lag.time.max.ms,会被移出ISR。观察ISR变化,是判断副本健康度的重要方式。

四、数据保留策略:数据留多久、留多少

Kafka不会因为消息被消费就删除数据,而是按保留策略清理。常见配置有两类:

  • retention.ms / retention.bytes:按时间或总大小保留,超过阈值后删除最旧的段文件。例如retention.ms=604800000表示保留7天。
  • cleanup.policy:delete表示删除,compact表示日志压缩,只保留每个Key的最新值,适合变更日志、状态存储等场景。也可组合为delete,compact。

此外还有segment.bytes和segment.ms控制日志段滚动,只有当前段滚动后,旧段才更容易被清理。保留策略要结合下游消费延迟、重放需求和存储成本综合确定。

五、多分区主题规划与创建任务

假设有一个订单事件主题,业务要求:峰值写入约6万条/秒,单分区可稳定承载约2万条/秒;消费者组希望最多6个实例并行消费;允许1个Broker故障不丢数据;事件保留7天,用于问题排查和重放。据此规划:分区数取max(60000/20000, 6)=6;副本因子取3,配合min.insync.replicas=2;保留时间7天;Key使用order_id保证同一订单有序。

使用命令行创建主题:

kafka-topics.sh --bootstrap-server broker1:9092 \
  --create \
  --topic orders-topic \
  --partitions 6 \
  --replication-factor 3 \
  --config retention.ms=604800000 \
  --config cleanup.policy=delete \
  --config min.insync.replicas=2

创建后查看主题详情,确认分区、副本和配置:

kafka-topics.sh --bootstrap-server broker1:9092 \
  --describe --topic orders-topic

预期输出中应看到6个分区,每个分区有3个副本,Leader分布在不同Broker上,且Configs列包含retention.ms、cleanup.policy和min.insync.replicas。若副本因子大于Broker数量,创建会失败;若分区数设置过小,后续消费者扩容会受限。创建完成后,可用kafka-console-producer写入带Key的消息,再用kafka-console-consumer观察消息是否按Key进入同一分区,从而验证分区策略是否符合预期。

主题规划的核心不是记住参数名,而是把业务吞吐、并行度、可靠性和成本翻译成分区数、副本因子与保留策略。建议在测试环境先用小规模数据验证分区分布和ISR状态,再应用到生产。