Kafka:消息队列的原理与实战

0 阅读23分钟

Kafka 的架构设计遵循“分布式、分区、多副本”原则,其核心在于将数据流(Topic)拆解为并行单元(Partition)进行水平扩展

Kafka 的架构本质是一个分布式的提交日志系统。它通过“分区”解决了并发瓶颈,通过“副本”解决了高可用问题,通过“消费者组”解决了数据复用问题,是现代数据管道和实时流处理的主流技术。

  • 吞吐优先:利用顺序 I/O 和 PageCache 规避磁盘随机读写瓶颈。
  • 水平扩展:通过增加 Partition 和 Broker 线性提升吞吐量。
  • 解耦与缓冲:作为生产者与消费者之间的异步缓冲层,抵御流量洪峰。
  • 多租户广播:通过 Consumer Group 机制,实现一份数据被多个业务系统独立消费(Pub-Sub)。

1. Kafka架构设计

1.1 架构全景图

组件角色与职责关键特性
Producer消息生产者通过 Key 决定消息发往哪个 Partition(负载均衡)
Consumer消息消费者以 Consumer Group 为单位,每个 Partition 只能被组内一个 Consumer 消费
BrokerKafka 服务器节点存储数据,组成集群;无需主从,通过 Zookeeper/KRaft 协调
Topic逻辑数据分类如 order_events,是消息的集合
Partition物理数据分片Topic 的并行单元,每个 Partition 是一个有序、不可变的日志
Replica副本每个 Partition 有多个副本(Leader/Follower),Leader 负责读写,Follower 同步备份
Zookeeper / KRaft元数据与协调中心管理 Broker 注册、Leader 选举、Consumer Offset(新版用 KRaft 替代 ZK)

1.2 数据流与存储机制

  1. 写入流程(Producer → Broker)
  • 路由:Producer 根据 Key 哈希或轮询,将消息发送到 Topic 的特定 Partition。
  • 追加日志:消息到达 Broker 后,以顺序追加(Append-Only) 的方式写入 Partition 日志文件。
  • 持久化:消息并非立即落盘,而是先写入 PageCache,由操作系统异步刷盘,兼顾高性能与持久性。

2. 消费流程(Broker → Consumer)

  • 拉取模式:Consumer 主动向 Broker 拉取(Pull)消息,可控制消费速率。
  • Offset 管理:Consumer Group 维护消费位移(Offset),标记已消费位置,支持at-least-once、at-most-once、exactly-once语义。
  • Rebalance:当 Consumer 加入或离开 Group 时,触发重新分配 Partition,实现自动容错。

3. 高可用机制(Replication)

  • Leader/Follower:每个 Partition 有一个 Leader 和多个 Follower。所有读写仅通过 Leader。
  • ISR(In-Sync Replicas) :与 Leader 保持同步的副本集合。只有 ISR 中的副本才有资格竞选 Leader。
  • 故障转移:若 Leader 宕机,ZooKeeper/KRaft 会从 ISR 中选举新的 Leader,保证服务不间断。

1.3 ZooKeeper 与 KRaft

模式架构特点适用场景
ZooKeeper 模式依赖外部 ZK 集群进行元数据管理旧版本(Kafka < 3.3),运维复杂
KRaft 模式内置元数据仲裁机制,去外部依赖新版本(Kafka ≥ 3.3),简化部署,提升稳定性

2. Kafka数据流转

2.1 生产者:向 Kafka 发送消息

生产者是将数据发送到 Kafka 主题的应用程序。它们能智能地决定将每条消息发送到何处。

生产者逻辑:

  1. 分区选择

如果消息有键(key),则使用 hash(key) % partition_count计算分区
如果无键,则使用轮询(round-robin)分配
也可以使用自定义的分区器逻辑

2. 交付保证

  • acks=0:发送即忘(最快,可靠性最低)
  • acks=1:等待领导者确认(平衡型)
  • acks=all:等待所有副本确认(最慢,可靠性最高)

生产者代码示例

Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<>(props);
// 发送消息到 user-events 主题
ProducerRecord<String, String> record = 
    new ProducerRecord<>("user-events", "user123", "{"action": "purchase"}");
producer.send(record);

2.2 消费者:从 Kafka 读取消息

消费者从 Kafka 主题读取数据。与传统消息系统在消费后即删除消息不同,Kafka 会保留消息,允许多个消费者独立地读取相同的数据。

消费者组

属于同一组的消费者共享工作负载

每个分区在同一时间只能被组内的一个消费者消费

不同的消费者组可以独立地消费相同的数据

消费者代码示例

Properties props = new Properties();
props.put("bootstrap.servers", "broker1:9092,broker2:9092");
props.put("group.id", "user-analytics-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
Consumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("user-events"));
while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        System.out.printf("Consumed: key=%s, value=%s, partition=%d, offset=%d%n",
                         record.key(), record.value(), record.partition(), record.offset());
    }
}

2.3 生产者与消费者:如何“对齐”

生产者与消费者,如何管理offset?

在 Kafka 中,生产者不管理 Offset消费者负责管理 Offset,但两者的管理机制和目的完全不同。下面是详细说明:

一、生产者端:不管理 Offset

生产者不关心 Offset,只负责发送消息。它管理的是消息确认机制,这间接影响了消息的持久化位置。

生产者确认机制(acks)

配置含义可靠性性能
acks=0不等待确认,只管发送最低(可能丢失)最高
acks=1等待 Leader 写入成功确认中等(Leader 宕机可能丢失)中等
acks=all/acks=-1等待所有 ISR 副本写入成功确认最高(强一致)最低

生产者代码示例

// 设置高可靠性配置
props.put("acks", "all"); // 等待所有副本确认
props.put("retries", 3);  // 失败重试3次
props.put("max.in.flight.requests.per.connection", 1); // 保证顺序

二、消费者端:Offset 管理的核心

消费者全权负责 Offset 的管理,这是 Kafka 消费语义的核心。

Offset 存储位置:

存储方式位置管理方特点
自动提交Kafka 内部 Topic:__consumer_offsetsKafka 自动管理默认方式,简单但有风险
手动提交__consumer_offsets或 外部存储(如数据库)消费者应用控制精确控制,避免重复/丢失

Offset 提交方式对比:

提交方式配置特点适用场景
自动提交enable.auto.commit=trueauto.commit.interval.ms=5000每5秒自动提交,可能重复消费允许少量重复的业务
同步手动提交enable.auto.commit=false调用 consumer.commitSync()提交成功才继续,性能较低金融、交易等强一致性场景
异步手动提交enable.auto.commit=false调用 consumer.commitAsync()不阻塞,性能高,失败不重试大部分业务场景
按记录提交处理一条提交一次最安全,性能最差极少使用

手动提交代码示例

// 禁用自动提交
props.put("enable.auto.commit", "false");

try {
    while (true) {
        ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
        for (ConsumerRecord<String, String> record : records) {
            // 处理消息
            processMessage(record);

            // 同步提交(确保提交成功)
            // consumer.commitSync();
        }
        // 批量异步提交(推荐)
        consumer.commitAsync();
    }
} catch (Exception e) {
    // 处理异常
} finally {
    try {
        // 最后同步提交确保成功
        consumer.commitSync();
    } finally {
        consumer.close();
    }
}

三、高级 Offset 管理策略

  1. 自定义 Offset 存储

将 Offset 存储到外部系统(如 MySQL、Redis),实现精确一次(Exactly-Once) 处理:

// 从数据库获取上次保存的 Offset
long offset = loadOffsetFromDB(topic, partition);
// 指定从该 Offset 开始消费
consumer.seek(new TopicPartition(topic, partition), offset);

while (true) {
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records) {
        processMessage(record);
        // 处理成功后,保存 Offset 到数据库
        saveOffsetToDB(record.topic(), record.partition(), record.offset() + 1);
    }
}

2. 消费语义控制

语义实现方式特点
至少一次(At-Least-Once)先处理消息,后提交 Offset可能重复,但不会丢失
至多一次(At-Most-Once)先提交 Offset,后处理消息可能丢失,但不会重复
精确一次(Exactly-Once)事务或幂等生产者+外部存储不丢失不重复,实现复杂

3. Offset 重置策略

当消费者组第一次启动或 Offset 失效时,可配置重置行为:

# 配置项
auto.offset.reset=latest|earliest|none

# latest:从最新位置开始消费(默认)
# earliest:从最早位置开始消费
# none:找不到 Offset 时抛出异常

3. 理解几个关键概念

Kafka 本质上是一个高吞吐、分布式、基于发布-订阅模型的实时消息系统。它通过“日志(Log)”这一底层数据结构,解决了海量数据流转的难题。

3.1 三大基础角色

这是理解数据流向的基石:

Producer(生产者)数据到 Kafka 的应用。
Consumer(消费者)数据并处理的应用。
Broker(服务器节点) :Kafka 集群中的单个实例,负责存储和转发消息。

3.2 数据组织与存储

Topic(主题)

  • 数据的逻辑分类,类似于数据库的表名。生产者发送消息时必须指定 Topic,消费者也按 Topic 订阅。

Partition(分区)

Topic 的物理分片,这是 Kafka 实现高并发和高扩展性的核心。
顺序性:消息在单个 Partition 内有序(全局无序)。
并行度:Partition 是并发的最小单位。Consumer 的吞吐量上限由它消费的 Partition 数量决定。
偏移量(Offset) :Partition 中每条消息的唯一 ID(类似数组下标),由 Kafka 自动维护。

Log(日志)

Partition 在磁盘上的物理存储形式,表现为只能追加(Append-Only)的文件。这种设计决定了 Kafka 的高性能——写入就是顺序写,读取是顺序读。

3.3 消费与保障机制

  1. Consumer Group(消费组)
  • 一组协同工作的消费者,是 Kafka 实现“负载均衡”和“队列/发布订阅”模式的关键。
  • 组内竞争:一个 Partition 在同一时间只能被组内一个 Consumer 消费。
  • 组间独立:不同 Consumer Group 消费同一 Topic 互不影响(实现广播)。

2. Replication(副本)

  • 数据高可用的保障。每个 Partition 有多个副本(Leader 和 Follower)。
  • Leader:负责处理所有读写请求。
  • Follower:异步同步 Leader 的数据。Leader 挂掉后,Follower 自动竞选成为新 Leader。

4. 理解几组关键关系

4.1 Topic与Partition的关系

Topic 和 Partition 是 Kafka 中逻辑与物理的关系。你可以把 Topic 看作数据库中的“表”,而 Partition 是这张表底层的“物理分片”。

一、核心关系:一对多的包含关系

一个 Topic 必须包含至少 1 个 Partition,一个 Partition 必须属于且仅属于一个 Topic。

  • Topic(逻辑分类) :数据的主题或类别,例如 order_events。它是面向业务和开发者的逻辑概念。
  • Partition(物理分片) :Topic 在物理存储上的实际分片。每个 Partition 是一个独立的、有序的日志文件。

二、关键特性与约束

  1. 消息顺序性
  • Partition 内有序:在单个 Partition 内部,消息严格按照写入顺序(Offset)排列,Kafka 保证此顺序。
  • Partition 间无序:跨 Partition 的消息没有全局顺序。如果你需要某类消息(如同一个用户 ID)严格有序,必须通过 Key 将它们路由到同一个 Partition。

2. 并行度与扩展性

  • Partition 数量决定最大并行度:一个 Consumer Group 中,同时消费的 Consumer 实例数量不能超过 Partition 数量

例如:Topic 有 3 个 Partition,Consumer Group 最多只能有 3 个 Consumer 同时工作(每个处理 1 个 Partition)。如果有第 4 个 Consumer,它会处于空闲状态。

  • 水平扩展:增加 Partition 数量可以提升 Topic 的吞吐量上限。

3. 数据分布与路由

  • Producer 路由:Producer 发送消息时,通过 Key 的哈希值(或轮询)决定消息写入哪个 Partition。
  • 物理隔离:不同 Partition 可以分布在不同的 Broker 节点上,实现负载均衡。

三、设计决策参考

场景建议策略
需要高吞吐增加 Partition 数量(如 12, 24, 32)
需要严格顺序使用 Key 确保相关消息进入同一 Partition
消费者扩展Partition 数应 ≥ 预期的最大消费者实例数

最佳实践:Partition 数量在创建 Topic 时设定,后期修改(增加)虽然可以,但可能会导致 Key 的哈希分布变化,建议初期预留一定余量(如 6-12 个)。

4.2 Topic与key的关系

Topic 是消息的逻辑分类(“去哪”),Key 是消息的路由标签(“怎么去”)。Key 本身不独立存在,它依附于 Topic,用于决定消息被发送到 Topic 下的哪一个 Partition

一、基础定义

Topic(主题) :消息的逻辑集合,类似数据库的“表名”。
Key(键) :消息的元数据,用于控制消息在 Topic 内的分布逻辑。

二、Key 如何影响 Topic 内的路由

Key 的核心作用是决定消息进入 Topic 的哪个 Partition,从而影响顺序性数据局部性

Key 设置情况路由逻辑(Partition 选择)典型应用场景
Key = null轮询(Round-Robin)发送到所有 Partition。吞吐量优先,无需顺序保障。
Key ≠ nullhash(key) % partition_num,相同 Key 必进同一 Partition。保证同一用户、订单 ID 的消息顺序。

三、关系本质:逻辑与物理的映射

Topic 是逻辑容器,Key 是物理路由策略的输入参数。
无 Key:Topic 只是一个数据池,消息均匀分布。
有 Key:Topic 被 Key 划分为多个“逻辑子流”(Partition),实现了数据分片(Sharding)

四、设计误区与最佳实践

Key 不是必选项:如果不需要顺序或聚合,建议不设 Key 以获得最佳吞吐。
热点 Key 风险:如果某个 Key 的数据量极大(如“默认用户”),会导致单个 Partition 成为瓶颈。
Key 的选择:优先使用业务主键(如 user_id、order_id)作为 Key,而非随机值。

4.3 Topic 与消费者组的关系

Topic 与消费者组(Consumer Group)的关系,本质上是  “广播内容”与“订阅观众”  的关系。它们之间是完全解耦的,一个 Topic 可以被多个消费者组独立消费,互不影响。

一、核心关系:一对多的独立订阅

  • 一个 Topic 可以被 0 个、1 个或多个消费者组同时订阅
  • 每个消费者组都会独立、完整地消费该 Topic 的所有消息

二、多消费者组场景(Pub-Sub 模式)

  • 这是 Kafka 最强大的特性之一:一份数据,多份消费
    假设你有一个 user_login_topic,记录了所有用户的登录事件:
  • 消费者组 A(数据分析团队):消费该 Topic,计算实时 UV/PV,写入数据仓库。
  • 消费者组 B(风控团队):消费该 Topic,检测异常登录行为,触发告警。
  • 消费者组 C(推送服务团队):消费该 Topic,判断用户是否长时间未登录,触发召回 Push。

这三个消费者组互不知晓对方的存在,各自维护独立的消费进度(Offset),互不干扰。

三、消费者组内部机制(Queue 模式)

虽然 Topic 是广播,但同一个消费者组内部是竞争关系,实现负载均衡。

维度同一个消费者组内(Competing Consumers)不同消费者组之间(Pub-Sub)
消息分配一个 Partition 只能被组内一个 Consumer 消费每个组都能收到全部消息
消费模式队列模式(负载均衡)发布订阅模式(广播)
Offset 管理组内共享进度(__consumer_offsets)各组独立维护进度
典型场景业务逻辑处理(需要扩容时增加 Consumer)数据复用(不同团队消费同一份数据)

四、关键设计约束

1 Partition 数量是并行度上限

一个消费者组内,有效的 Consumer 实例数量不能超过 Topic 的 Partition 数量。
例如:Topic 有 3 个 Partition,消费者组最多有 3 个 Consumer 在干活。如果有第 4 个 Consumer,它会处于空闲状态(Idle),直到有 Partition 被释放。

2 消费进度(Offset)独立

每个消费者组在 Kafka 的内部 Topic __consumer_offsets中独立记录自己的消费位置。

  • 消费者组 A 可能消费到了 Offset=1000。
  • 消费者组 B 可能因为重启,还在消费 Offset=500。

它们互不影响。

五、设计决策参考表

业务场景Topic 与 消费者组 设计策略
单一业务逻辑处理(如订单处理)1 个 Topic + 1 个消费者组,通过增加组内 Consumer 实例来扩容
数据复用(如一份日志多方使用)1 个 Topic + N 个消费者组,每个组对应一个下游业务
需要顺序保证确保同一类消息(相同 Key)进入同一 Partition,且该 Partition 在同一组内仅由一个 Consumer 处理
测试或调试使用独立的消费者组(如 test_group),避免干扰线上业务的消费进度

最佳实践:Topic 是数据的“生产者视图”,消费者组是数据的“消费者视图”。设计时应优先考虑“谁需要这份数据”,而不是“怎么消费这份数据”。

4.4 partition与 消费者组的关系

Consumer Group(消费者组)与 Partition(分区)的关系是 Kafka 并行处理与负载均衡的基石。简单来说:一个 Partition 在同一时刻只能被一个 Consumer Group 内的唯一 Consumer 消费
这种“一对一”的锁定关系,直接决定了系统的吞吐量和并发能力。

一、核心关系:抢占式消费

在同一个 Consumer Group 内,Partition 是分配的最小单位。你可以把 Partition 想象成“蛋糕”,Consumer 是“吃蛋糕的人”。
规则:每个 Partition 只能分配给组内的一个 Consumer。
结果组内 Consumer 的数量与 Partition 的数量直接决定了并发上限

二、数量关系的三种状态

这是面试和工作中最常考察的重点:

状态关系现象与影响
C = P消费者数 = 分区数理想状态。每个 Consumer 独占一个 Partition,资源被充分利用。
C < P消费者数 < 分区数部分 Consumer 需承担多个 Partition。此时虽然能运行,但部分消费者压力较大。
C > P消费者数 > 分区数有 Consumer 处于空闲状态。多出来的 Consumer 无法分配到 Partition,造成资源浪费。

关键结论一个 Consumer Group 的并发度上限等于该 Topic 的 Partition 数量。增加 Consumer 数量超过 Partition 数量不会提升性能。

三、为什么需要这种关系?

这种设计主要解决了两个核心问题:
顺序性保障:因为一个 Partition 只被一个 Consumer 处理,所以 Partition 内部的消息顺序得以严格保持(适用于订单流水等场景)。
负载均衡:Kafka 通过 Coordinator 自动将 Partition 均匀分配给组内的所有 Consumer,实现自动的负载均衡(Rebalance)。

四、Group 之间的隔离性

不同 Consumer Group 消费同一 Topic 是完全隔离的
Group A 和 Group B 可以同时消费 topic.order的所有 Partition。
这实现了“广播”效果:一条消息可以被多个不同业务(如风控、数仓)同时消费,互不干扰。

五、实战建议

规划分区数:创建 Topic 时,Partition 数量应至少等于你预计的最大 Consumer 数量,为未来扩容留足空间(分区数只能增不能减)。
避免闲置:尽量不要让 Consumer 数量长期大于 Partition 数量。
Rebalance:当 Consumer 加入或离开组时,会触发 Partition 的重新分配,此时消费会短暂暂停,这是正常现象。

4.5 Partition 与 Broker的关系

Partition(分区)与 Broker(服务器节点)的关系,是  “存储单元”与“物理载体”  的关系。你可以把 Broker 理解为书架,把 Partition 理解为。一个书架可以放很多本书,但同一本书的副本不能放在同一个书架上。

一、核心关系:多对多的物理分布

一个 Broker 可以存储多个 Partition,一个 Partition 的多个副本必须分布在不同的 Broker 上。

这是 Kafka 实现高吞吐(通过 Partition 拆分)和高可用(通过副本分布)的物理基础。

  1. 数据分布机制
    Partition 是存储实体:Topic 是逻辑概念,数据实际存储在 Partition 的日志文件中。
    Broker 是存储节点:Partition 的物理文件最终落在 Broker 的磁盘上。
  2. 副本放置策略(关键)

每个 Partition 有多个副本(Replica),Kafka 强制要求同一个 Partition 的所有副本不能放在同一个 Broker 上。这是为了确保一台机器宕机时,数据依然可用。

示例

假设你有一个 3 节点的 Kafka 集群(Broker-0, Broker-1, Broker-2),一个 Partition P0有 3 个副本,它的分布必须是:

  • P0的 Leader 副本在 Broker-0
  • P0的 Follower 副本在 Broker-1
  • P0的 Follower 副本在 Broker-2

二、读写机制:Leader 与 Follower

Partition 副本分为 Leader 和 Follower,读写操作只与 Leader Broker 交互。

角色职责读写权限
Leader Replica处理所有 Producer 和 Consumer 的读写请求可读可写
Follower Replica从 Leader 异步/同步拉取数据,保持同步只同步,不提供读服务

关键点

写放大:Producer 只向 Leader Broker 写入数据,Leader 负责将数据复制到 Follower。

读隔离:Consumer 默认只从 Leader Broker 读取数据(除非配置了 Follower 读取,但通常不建议)。

三、容量与扩展性设计

  1. 分区数 vs Broker 数
  • Partition 数量决定了 Topic 的最大并行度(一个 Partition 只能被一个 Consumer 消费)。
  • Broker 数量决定了集群的总存储容量故障容忍度

2. 容量规划公式

为了保证集群健康,通常建议遵循以下经验法则:

  1. 单个 Broker 上的 Partition 总数 ≤ 2000 - 4000

计算逻辑

  • 假设你有 10 个 Topic,每个 Topic 有 100 个 Partition,副本因子为 3。
  • 那么整个集群的 Partition 总数为 10 × 100 × 3 = 3000。
  • 如果均匀分布在 3 个 Broker 上,每个 Broker 承载 3000 / 3 = 1000个 Partition,这在安全范围内。
    如果单个 Broker 上的 Partition 数量过多(如超过 5000),会导致:
    文件句柄耗尽、Leader 选举变慢、恢复时间变长
    四、故障转移(Failover)流程
    当 Broker 宕机时,Partition 与 Broker 的关系会动态调整:
  • 检测:ZooKeeper/KRaft 检测到 Broker-0 失联。
  • 选举:对于所有 Leader 在 Broker-0 上的 Partition,从 ISR(同步副本列表)中选举一个新的 Leader(例如 Broker-1)。
  • 恢复:Producer 和 Consumer 自动切换到新的 Leader Broker 继续工作。

注意:如果宕机的 Broker 是 Follower,通常对服务无影响,因为读写只依赖 Leader。

五、设计决策参考表

场景策略目标
提高吞吐量增加 Partition 数量提升并行处理能力
提高容错性增加 Broker 数量,并确保副本因子 ≤ Broker 数确保宕机时仍有副本可用
均衡负载确保 Partition 均匀分布在所有 Broker 上避免单节点热点

最佳实践:在创建 Topic 时,Partition 数量建议设置为 Broker 数量的整数倍(如 3 个 Broker,设置 6、9、12 个 Partition),这样数据分布会更均匀。

5. 如何设计Topic

5.1 命名规范(清晰、一致)

推荐结构:<环境>.<数据领域>.<数据来源>.<事件/对象>.<数据格式>
示例
prod.log.app.user_click.avro
prod.metric.server.cpu_usage.json
prod.biz.order.order_created.avro
简化版(常用):<业务线>.<数据源>.<事件名>
trade.pc.payment_success

5.2 分区数(Partition)设计 - 决定并发度

核心公式:分区数 ≈ 目标吞吐量 / 单个分区吞吐量

单个分区吞吐量经验值:约 10-50 MB/s。

估算步骤

  1. 评估峰值写入速率:例如,高峰时每秒 10 万条消息,每条平均 1KB,则吞吐量为 100,000 msg/s * 1KB ≈ 100 MB/s。
  2. 计算最小分区数:100 MB/s ÷ 20 MB/s/分区 ≈ 5 个分区。
  3. 预留缓冲:考虑未来增长,可设定为 2-4 倍,例如 5 * 3 = 15 个分区。通常选择 2 的 N 次方,如 16。

关键限制分区数只能增加,不能减少。所以初期可适度多分,但不宜过多(每个分区都有开销)。

5.3 副本数(Replication Factor) - 决定可靠性

建议值

  • 开发/测试环境:1(节约资源)。
  • 生产环境:至少为 3。这是保证高可用的标准配置,允许同时宕机 2 个节点而不丢失数据。

5.4 数据格式(序列化)

  • 推荐 Avro 或 Protobuf绝不推荐纯字符串 JSON

原因:它们 Schema 清晰、压缩率高、兼容性好,便于上下游系统解析。配合 Schema Registry(如 Confluent Schema Registry)使用最佳。

5.5 经典设计模式

模式一:按数据来源/设备分离

topic.app_click(App端点击)
topic.web_click(Web端点击)
topic.iot_sensor(物联网传感器)

优点:来源隔离,互不影响,便于按来源分配资源和设置策略。

模式二:按数据优先级/延迟要求分离

topic.high_priority_realtime(延时<1s,分区多,副本多)
topic.low_priority_batch(延时允许分钟级,可压缩,保留时间长)

优点:资源分配更合理,保障核心业务。

模式三:原始数据与标准化数据分离(推荐架构)

1. 原始上报层 (Raw Ingestion):
   - `topic.raw_app_log` (所有App原始日志,格式不一,保留7天)
   - 消费者:一个统一的“标准化处理服务”。

2. 标准化数据层 (Standardized Data):
   - `topic.biz_user_event` (处理后的标准用户事件,Avro格式,保留30天)
   - `topic.biz_app_performance` (处理后的性能数据)
   - 消费者:数仓、风控、推荐等各业务系统。

优点:解耦,原始数据可追溯,下游用标准数据,清爽稳定。

5.6 两种设计策略

设计维度起步阶段(稳中求进,快速验证)扩张阶段(精细治理,保障稳定)
核心目标低成本启动、架构清晰、便于迭代支撑高并发、保障数据质量、多团队协作
命名规范极简结构:<业务线>.<事件类型>(例:trade.order_created)完整结构:<环境>.<数据领域>.<来源>.<事件>.<格式>(例:prod.trade.app.payment_success.avro)
分区策略固定数量:从 6 个分区 起步(兼顾 扩展性与高性能)动态评估:业务并行度:如按 user_id哈希,至少 30 个并发单元 → 分区数 ≥ 30吞吐量规划:(峰值吞吐 / 20MB/s) * 缓冲系数(2-3)
副本数生产环境 直接设为 3(建立高可用基线)分级设置:• 核心业务 Topic → 可提升至 4 或 5• 非核心 Topic → 保持 3
数据格式必须使用 Protobuf/Avro + Schema Registry(杜绝 JSON 字符串)强制化 + 流程管控:• 所有 Topic 强制注册 Schema• Schema 变更需走审批与兼容性检查流程
保留时间统一策略:默认 30 天(简化管理)分级策略:• 热数据(订单/支付):30 天• 温数据(用户行为):15 天• 冷数据(调试日志):8 天• 永久数据:归档至廉价存储
归档机制核心数据先行:仅对支付、用户等核心事件,配置自动化归档至 S3分级自动化体系:实时归档:核心数据实时入数据湖(Iceberg/Hudi)定时归档:所有数据按保留策略过期前批量压缩转储成本可视:建立存储费用监控看板
Topic 数量与治理粗粒度,少量 Topic:按数据领域划分(如 user.events, order.events),共 3-5 个细粒度 + 分层架构:原始接入层:topic.raw_标准数据层:topic.std_衍生数据层:topic.dwd<agg_name>治理措施:• Topic 申请审批流程• 血缘图谱与依赖管理• “僵尸 Topic”定期审计下线

5.7 Checklist

  1. 命名:是否清晰、符合团队规范?
  2. 分区:数量是否足够支撑未来 1-2 年的峰值流量?(建议 6/12/16/24/32…)
  3. 副本:生产环境是否 ≥ 3?
  4. 格式:是否使用 Avro/Protobuf 并配备了 Schema Registry?
  5. 保留时间:是否根据数据价值设置(如 7天、30天、永久)?
  6. 归档:重要数据是否有下游归档到 HDFS/S3 的机制?

最后建议:在项目初期,可以先按“数据来源”划分 Topic,并采用  “原始-标准化”两层结构。这个模式能很好地应对初期的复杂性和未来的变化。可以先创建 1-2 个核心 Topic 跑通流程,再按业务扩展。