
在上一篇中我们完成了从 ZooKeeper 到 KRaft 的迁移。迁移改变的是元数据如何管理但没有改变 Kafka 最根本的数据模型——Topic 是逻辑分类Partition 是物理并行单元Offset 是分区内的唯一标识。然而正是这三个看似简单的概念决定了 Kafka 应用中最容易被忽视、后果最严重的几个决策分区数为什么“事实上永久”、副本因子为什么必须在创建时定好、以及“只在分区内有序”这条铁律如何倒逼你的 key 设计。 本文从数据模型的核心语义讲起深入拆解分区数的永久性、副本因子的耐久性含义、以及五种分区策略的取舍帮你建立建 Topic 之前必须想清楚的工程判断。一、Topic、Partition、Offset三层语义1.1 Topic逻辑分类Topic 是消息的逻辑分类——业务上的一组相关消息。例如 order-events、user-activity、payment-callbacks。Topic 本身不决定并行度或存储分布它只是一个命名空间。1.2 Partition物理并行单元Topic 在物理上被划分为一个或多个 Partition。Partition 是 Kafka 实现并行处理和水平扩展的基本单位。每个 Partition 是一个有序的、不可变的、追加写的消息序列。Partition 的数量决定了两个上限生产端的并行写入能力消息可以同时写入多个分区。消费端的并行处理能力一个消费者组中每个分区只能被一个消费者实例消费。分区数就是消费者并行度的天花板。1.3 Offset分区内的唯一标识每条消息在所属分区内拥有一个唯一的、单调递增的 Offset。Offset 是消费者跟踪“我消费到了哪里”的标识。一个必须刻在脑子里的规则Offset 只在单个分区内唯一跨分区不存在全局有序的 Offset。不同分区的数据完全独立分区 0 的 Offset 100 和分区 1 的 Offset 100 没有任何关系。二、有序性只在分区内保证这是铁律Kafka 的唯一顺序保证是“分区内有序”。跨分区永远不保证顺序。这条规则的含义是如果你需要一组相关消息按顺序处理它们必须被路由到同一个分区。而路由到同一个分区的唯一可靠方式是给这些消息相同的 key。// 所有 orderId ORD-123 的事件都会进入同一个分区producer.send(newProducerRecord(order-events,ORD-123,event));默认的分区器使用 murmur2 哈希murmur2(key) % number_of_partitions。相同的 key 必然得到相同的哈希值因此必然进入同一个分区顺序得到保证。如果没有 keynull keyKafka 2.4 默认使用 sticky partitioner——先批量发往同一个分区再切换而不是严格的轮询。这优化了吞吐和压缩但不提供任何顺序保证。三、分区数一个“事实上永久”的决策分区数可以增加但永远不能减少。 这是 Kafka 数据模型中最硬的约束之一。3.1 增加分区会破坏 key 的顺序关键在于哈希映射hash(key) % N。当 N 从 12 变成 24 时同一个 key 的哈希值对新的 N 取模结果可能完全不同。textkey “ORD-123”hash 98765412 个分区987654 % 12 6 → 分区 624 个分区987654 % 24 18 → 分区 18同一个订单的历史事件可能分散到不同的分区消费端就不再能按顺序处理它们。这就是为什么增加分区会破坏基于 key 的排序而且没有任何技术手段可以弥补——哈希映射一旦改变旧数据的位置不会迁移。3.2 分区数的选择策略选择“多因子”数值避免质数。 分区数应该是多个小整数的乘积为消费者扩缩容留出灵活空间。每个 Broker 的分区数应控制在 4,000 以下。超过这个阈值后Broker 恢复时间、Controller 元数据管理开销都会显著恶化。四、副本因子耐久性的第一道防线副本因子replication factor在 Topic 创建时就应该定为 3 或更高而不是“先设为 1以后再加”。为什么修改副本因子需要分区重分配——在网络上复制全量数据。对于一个数十 GB 的 Topic这是一次昂贵且有风险的操作需要控制迁移速率否则可能打满网络带宽、影响正常业务流量。耐久性的完整配置是三个参数的组合仅仅设置 acksall 是不够的。如果 ISR 收缩到只剩 1 个副本acksall 在行为上就退化成了 acks1——只有一个副本确认leader 故障时数据仍然可能丢失。min.insync.replicas2 是让 acksall 保持其语义的必要条件当 ISR 中可用的副本数少于 2 时生产者会收到 NotEnoughReplicasException而不是在一个不安全的单副本上“成功”写入。五、五种分区策略速览Kafka 提供了多种分区策略每种在有序性、吞吐量和热点风险之间有不同的取舍。Key-based 的热点风险是实际生产中最常见的问题如果某个 key 的消息量远超其他 key例如一个超级大客户的 customer_id那个分区会成为瓶颈其他分区却很空闲。对于这种“热点 key”需要评估是否接受按 key 有序的代价——如果有序性不是必需的可以考虑用 key 随机后缀 打散或者使用自定义分区器。六、小结三层语义Topic 是逻辑分类Partition 是物理并行单元决定消费并行度上限Offset 只在分区内唯一。有序性铁律只在分区内有序跨分区永不保证。需要有序的消息必须用相同的 key。分区数“事实上永久” 可以增加不能减少增加分区会破坏 key 的哈希映射从而破坏顺序没有补救手段。选择多因子数值12、24、60避免质数。每 Broker 分区数控制在 4,000 以下。副本因子创建时定为 3后续修改需要全量数据复制。耐久性三要素replication.factor3 min.insync.replicas2 acksall。仅设 acksall 而不设 min.insync.replicas 是不完整的。分区策略Key-based 保证有序但有热点风险Sticky无 key 默认吞吐最高但无顺序Custom 用于特殊业务逻辑。