Kafka | 学习笔记

文章目录

  • [0. Kafka 一句话总览](#0. Kafka 一句话总览)
  • [1. Kafka 为什么出现?](#1. Kafka 为什么出现?)
  • [2. Kafka 基础架构](#2. Kafka 基础架构)
  • [3. Kafka 快速命令复习](#3. Kafka 快速命令复习)
  • [4. Kafka Producer 生产者](#4. Kafka Producer 生产者)
    • [4.1 Producer 发送消息整体流程](#4.1 Producer 发送消息整体流程)
    • [4.2 RecordAccumulator 是什么?](#4.2 RecordAccumulator 是什么?)
    • [4.3 异步发送与同步发送](#4.3 异步发送与同步发送)
    • [4.4 Producer 重要参数](#4.4 Producer 重要参数)
  • [5. 生产者分区策略](#5. 生产者分区策略)
    • [5.1 为什么要分区?](#5.1 为什么要分区?)
    • [5.2 默认分区策略](#5.2 默认分区策略)
      • [1)如果指定了 partition](#1)如果指定了 partition)
      • [2)如果没有指定 partition,但指定了 key](#2)如果没有指定 partition,但指定了 key)
      • [3)如果既没有 partition,也没有 key](#3)如果既没有 partition,也没有 key)
    • [5.3 自定义分区器](#5.3 自定义分区器)
    • [5.4 分区策略面试模板](#5.4 分区策略面试模板)
  • [6. Producer 如何提高吞吐量?](#6. Producer 如何提高吞吐量?)
    • [6.1 四个核心参数](#6.1 四个核心参数)
      • [1)调大 `buffer.memory`](#1)调大 buffer.memory)
      • [2)调大 `batch.size`](#2)调大 batch.size)
      • [3)设置 `linger.ms`](#3)设置 linger.ms)
      • [4)开启压缩 `compression.type`](#4)开启压缩 compression.type)
    • [6.2 生产者吞吐量调优模板](#6.2 生产者吞吐量调优模板)
    • [6.3 面试回答模板](#6.3 面试回答模板)
  • [7. Kafka 数据可靠性:acks、ISR、副本](#7. Kafka 数据可靠性:acks、ISR、副本)
    • [7.1 acks 三种级别](#7.1 acks 三种级别)
    • [7.2 ISR 是什么?](#7.2 ISR 是什么?)
    • [7.3 数据完全可靠的条件](#7.3 数据完全可靠的条件)
    • [7.4 可靠性面试模板](#7.4 可靠性面试模板)
  • [8. Kafka 数据重复、幂等性和事务](#8. Kafka 数据重复、幂等性和事务)
    • [8.1 三种消息语义](#8.1 三种消息语义)
      • [At Most Once:最多一次](#At Most Once:最多一次)
      • [At Least Once:至少一次](#At Least Once:至少一次)
      • [Exactly Once:精准一次](#Exactly Once:精准一次)
    • [8.2 为什么会重复?](#8.2 为什么会重复?)
    • [8.3 幂等性原理](#8.3 幂等性原理)
    • [8.4 幂等性的限制](#8.4 幂等性的限制)
    • [8.5 开启幂等性](#8.5 开启幂等性)
    • [8.6 事务机制](#8.6 事务机制)
    • [8.7 事务相关 API](#8.7 事务相关 API)
    • [8.8 Transaction Coordinator](#8.8 Transaction Coordinator)
    • [8.9 幂等性和事务面试模板](#8.9 幂等性和事务面试模板)
  • [9. Kafka 数据有序性](#9. Kafka 数据有序性)
    • [9.1 Kafka 是否保证有序?](#9.1 Kafka 是否保证有序?)
    • [9.2 如何保证某类消息有序?](#9.2 如何保证某类消息有序?)
    • [9.3 为什么可能乱序?](#9.3 为什么可能乱序?)
    • [9.4 保证单分区有序的条件](#9.4 保证单分区有序的条件)
    • [9.5 有序性面试模板](#9.5 有序性面试模板)
  • [10. Kafka Broker 工作机制](#10. Kafka Broker 工作机制)
    • [10.1 Broker 启动后做了什么?](#10.1 Broker 启动后做了什么?)
    • [10.2 Controller 是什么?](#10.2 Controller 是什么?)
    • [10.3 Leader 选举流程](#10.3 Leader 选举流程)
    • [10.4 Broker 面试模板](#10.4 Broker 面试模板)
  • [11. Kafka 副本机制、HW、LEO](#11. Kafka 副本机制、HW、LEO)
    • [11.1 副本机制](#11.1 副本机制)
    • [11.2 LEO 是什么?](#11.2 LEO 是什么?)
    • [11.3 HW 是什么?](#11.3 HW 是什么?)
    • [11.4 Follower 故障如何处理?](#11.4 Follower 故障如何处理?)
    • [11.5 Leader 故障如何处理?](#11.5 Leader 故障如何处理?)
  • [12. Kafka 存储机制](#12. Kafka 存储机制)
    • [12.1 Topic、Partition、Segment 的关系](#12.1 Topic、Partition、Segment 的关系)
    • [12.2 Segment 里有哪些文件?](#12.2 Segment 里有哪些文件?)
    • [12.3 为什么 Kafka 使用 Segment?](#12.3 为什么 Kafka 使用 Segment?)
    • [12.4 稀疏索引](#12.4 稀疏索引)
  • [13. Kafka 日志清理策略](#13. Kafka 日志清理策略)
    • [13.1 delete 删除策略](#13.1 delete 删除策略)
    • [13.2 compact 压缩策略](#13.2 compact 压缩策略)
  • [14. Kafka 为什么快?](#14. Kafka 为什么快?)
    • [14.1 分区并行](#14.1 分区并行)
    • [14.2 顺序写磁盘](#14.2 顺序写磁盘)
    • [14.3 PageCache](#14.3 PageCache)
    • [14.4 零拷贝](#14.4 零拷贝)
    • [14.5 批量发送](#14.5 批量发送)
    • [14.6 压缩](#14.6 压缩)
    • [14.7 稀疏索引](#14.7 稀疏索引)
    • [14.8 面试模板](#14.8 面试模板)
  • [15. Kafka Consumer 消费者](#15. Kafka Consumer 消费者)
    • [15.1 消费者基本流程](#15.1 消费者基本流程)
    • [15.2 Consumer 重要参数](#15.2 Consumer 重要参数)
    • [15.3 Consumer Group 是什么?](#15.3 Consumer Group 是什么?)
    • [15.4 Coordinator 是什么?](#15.4 Coordinator 是什么?)
    • [15.5 消费者组初始化流程](#15.5 消费者组初始化流程)
    • [15.6 Rebalance 是什么?](#15.6 Rebalance 是什么?)
    • [15.7 Rebalance 的问题](#15.7 Rebalance 的问题)
  • [16. 分区分配策略](#16. 分区分配策略)
    • [16.1 Range 策略](#16.1 Range 策略)
    • [16.2 RoundRobin 策略](#16.2 RoundRobin 策略)
    • [16.3 Sticky 策略](#16.3 Sticky 策略)
    • [16.4 CooperativeSticky 策略](#16.4 CooperativeSticky 策略)
    • [16.5 分区分配策略面试模板](#16.5 分区分配策略面试模板)
  • [17. Offset 机制](#17. Offset 机制)
    • [17.1 Offset 是什么?](#17.1 Offset 是什么?)
    • [17.2 Offset 存在哪里?](#17.2 Offset 存在哪里?)
    • [17.3 自动提交 Offset](#17.3 自动提交 Offset)
    • [17.4 手动提交 Offset](#17.4 手动提交 Offset)
      • [commitSync 同步提交](#commitSync 同步提交)
      • [commitAsync 异步提交](#commitAsync 异步提交)
    • [17.5 重复消费场景](#17.5 重复消费场景)
    • [17.6 漏消费场景](#17.6 漏消费场景)
    • [17.7 正确处理建议](#17.7 正确处理建议)
    • [17.8 auto.offset.reset](#17.8 auto.offset.reset)
    • [17.9 指定 Offset 消费](#17.9 指定 Offset 消费)
    • [17.10 指定时间消费](#17.10 指定时间消费)
    • [17.11 Offset 面试模板](#17.11 Offset 面试模板)
  • [18. Kafka 如何保证精准一次?](#18. Kafka 如何保证精准一次?)
    • [18.1 生产端精准一次](#18.1 生产端精准一次)
    • [18.2 Kafka 内部流转精准一次](#18.2 Kafka 内部流转精准一次)
    • [18.3 Kafka 到外部系统是否天然精准一次?](#18.3 Kafka 到外部系统是否天然精准一次?)
    • [18.4 精准一次面试模板](#18.4 精准一次面试模板)
  • [19. Kafka 消息积压如何处理?](#19. Kafka 消息积压如何处理?)
  • [20. Kafka Broker 核心参数](#20. Kafka Broker 核心参数)
  • [21. 自动创建 Topic 为什么生产环境建议关闭?](#21. 自动创建 Topic 为什么生产环境建议关闭?)
  • [22. 分区数如何设计?](#22. 分区数如何设计?)
    • [22.1 分区数影响什么?](#22.1 分区数影响什么?)
    • [22.2 分区数不是越多越好](#22.2 分区数不是越多越好)
    • [22.3 面试回答模板](#22.3 面试回答模板)
  • [23. 生产环境硬件配置估算](#23. 生产环境硬件配置估算)
    • [23.1 估算示例](#23.1 估算示例)
    • [23.2 服务器台数估算](#23.2 服务器台数估算)
    • [23.3 磁盘选择](#23.3 磁盘选择)
    • [23.4 内存选择](#23.4 内存选择)
    • [23.5 CPU 选择](#23.5 CPU 选择)
    • [23.6 网络选择](#23.6 网络选择)
  • [24. Kafka 总体吞吐量优化](#24. Kafka 总体吞吐量优化)
    • [24.1 生产者侧](#24.1 生产者侧)
    • [24.2 Broker 侧](#24.2 Broker 侧)
    • [24.3 消费者侧](#24.3 消费者侧)
    • [24.4 总体吞吐量面试模板](#24.4 总体吞吐量面试模板)
  • [25. Kafka 外部系统集成](#25. Kafka 外部系统集成)
    • [25.1 Flume 集成 Kafka](#25.1 Flume 集成 Kafka)
      • [Flume → Kafka](#Flume → Kafka)
      • [Kafka → Flume](#Kafka → Flume)
    • [25.2 Flink 集成 Kafka](#25.2 Flink 集成 Kafka)
      • [Flink → Kafka](#Flink → Kafka)
      • [Kafka → Flink](#Kafka → Flink)
    • [25.3 SpringBoot 集成 Kafka](#25.3 SpringBoot 集成 Kafka)
  • [26. Kafka 常见面试题模板](#26. Kafka 常见面试题模板)
    • [26.1 Kafka 是什么?](#26.1 Kafka 是什么?)
    • [26.2 Kafka 为什么吞吐量高?](#26.2 Kafka 为什么吞吐量高?)
    • [26.3 Kafka 如何保证消息不丢?](#26.3 Kafka 如何保证消息不丢?)
    • [26.4 Kafka 会不会重复消费?](#26.4 Kafka 会不会重复消费?)
    • [26.5 Kafka 如何保证顺序消费?](#26.5 Kafka 如何保证顺序消费?)
    • [26.6 Kafka 的 acks 有哪些?](#26.6 Kafka 的 acks 有哪些?)
    • [26.7 ISR 是什么?](#26.7 ISR 是什么?)
    • [26.8 Kafka 幂等性怎么实现?](#26.8 Kafka 幂等性怎么实现?)
    • [26.9 Kafka 事务解决什么问题?](#26.9 Kafka 事务解决什么问题?)
    • [26.10 Kafka 消费者组是什么?](#26.10 Kafka 消费者组是什么?)
    • [26.11 Rebalance 是什么?](#26.11 Rebalance 是什么?)
    • [26.12 Kafka Offset 是什么?存在哪里?](#26.12 Kafka Offset 是什么?存在哪里?)
    • [26.13 自动提交和手动提交 Offset 区别?](#26.13 自动提交和手动提交 Offset 区别?)
    • [26.14 Kafka 如何处理消息积压?](#26.14 Kafka 如何处理消息积压?)
    • [26.15 Kafka 为什么不直接删除已消费消息?](#26.15 Kafka 为什么不直接删除已消费消息?)
  • [27. 最后总复习脑图](#27. 最后总复习脑图)
  • [28. 面试最终背诵版](#28. 面试最终背诵版)
  • [29. 后端开发校招 Kafka 高频追问补充](#29. 后端开发校招 Kafka 高频追问补充)
    • [29.1 Kafka、RabbitMQ、RocketMQ 怎么选?](#29.1 Kafka、RabbitMQ、RocketMQ 怎么选?)
    • [29.2 为什么不用 MySQL 当消息队列?](#29.2 为什么不用 MySQL 当消息队列?)
    • [29.3 为什么不用 Redis List 当消息队列?](#29.3 为什么不用 Redis List 当消息队列?)
    • [29.4 Kafka 是 Pull 还是 Push?为什么?](#29.4 Kafka 是 Pull 还是 Push?为什么?)
      • [为什么使用 Pull?](#为什么使用 Pull?)
    • [29.5 Kafka 消息丢失怎么排查?](#29.5 Kafka 消息丢失怎么排查?)
      • [1)Producer 端丢失](#1)Producer 端丢失)
      • [2)Broker 端丢失](#2)Broker 端丢失)
      • [3)Consumer 端丢失](#3)Consumer 端丢失)
      • 面试回答模板
    • [29.6 Kafka 重复消费怎么解决?](#29.6 Kafka 重复消费怎么解决?)
    • [29.7 Kafka 是否支持延迟消息?](#29.7 Kafka 是否支持延迟消息?)
    • [29.8 Kafka 是否有死信队列?](#29.8 Kafka 是否有死信队列?)
    • [29.9 Kafka 分区数为什么只能增加,不能减少?](#29.9 Kafka 分区数为什么只能增加,不能减少?)
    • [29.10 KRaft 和 Zookeeper 是什么关系?](#29.10 KRaft 和 Zookeeper 是什么关系?)
    • [29.11 Kafka 线上需要监控哪些指标?](#29.11 Kafka 线上需要监控哪些指标?)
      • [Broker 侧](#Broker 侧)
      • [Producer 侧](#Producer 侧)
      • [Consumer 侧](#Consumer 侧)
      • 面试回答模板
    • [29.12 后端校招 Kafka 最终问题清单](#29.12 后端校招 Kafka 最终问题清单)

目标:以后复习 Kafka 面试时,只看这一份讲义,就能快速回忆起 Kafka 的核心概念、底层机制、生产者、Broker、消费者、可靠性、事务、Offset、消息积压、生产调优和常见面试回答。


0. Kafka 一句话总览

Kafka 本质上是一个 分布式、高吞吐、可持久化、基于发布/订阅模型的事件流平台

传统理解中,Kafka 是消息队列;更准确地说,Kafka 是事件流平台,既能做消息缓冲,也能做日志采集、实时数据管道、流式处理和系统解耦。

面试时可以这样回答:

Kafka 是一个分布式事件流平台,底层通过 Topic 对消息分类,通过 Partition 实现水平扩展和并行读写,通过副本机制保证高可用,通过消费者组实现并行消费,通过顺序写磁盘、PageCache、零拷贝、批量发送等机制实现高吞吐。它常用于日志采集、削峰填谷、系统解耦、异步通信和实时数据处理。


1. Kafka 为什么出现?

1.1 消息队列的三个核心作用

1)缓冲 / 削峰

生产者瞬时流量很大,消费者处理能力有限,如果直接打到后端系统,容易压垮系统。

Kafka 作为中间缓冲层:

text 复制代码
高峰请求 / 日志流量
        ↓
      Kafka
        ↓
后端消费者按能力消费

典型场景:

  • 秒杀系统削峰
  • 日志采集削峰
  • 双十一活动流量缓冲
  • 用户行为埋点缓冲

2)解耦

没有 MQ 时:

text 复制代码
系统 A → 系统 B
系统 A → 系统 C
系统 A → 系统 D

系统 A 要关心所有下游系统。

引入 Kafka 后:

text 复制代码
系统 A → Kafka → 系统 B
              → 系统 C
              → 系统 D

系统 A 只负责写 Kafka,下游系统自己订阅自己关心的 Topic。

好处:

  • 上下游独立演进
  • 新增消费者不影响生产者
  • 下游故障不直接拖垮上游
  • 数据源和处理系统解耦

3)异步通信

同步处理:

text 复制代码
用户注册 → 写数据库 → 发短信 → 返回结果

异步处理:

text 复制代码
用户注册 → 写数据库 → 写 Kafka → 返回结果
                         ↓
                      短信系统异步消费

好处:

  • 提高接口响应速度
  • 降低链路阻塞
  • 非核心流程异步化

1.2 点对点模式 vs 发布订阅模式

点对点模式

text 复制代码
Producer → Queue → Consumer

特点:

  • 消息被一个消费者消费后通常会删除
  • 一条消息一般只被一个消费者处理
  • 更像传统队列

发布订阅模式

text 复制代码
Producer → Topic → Consumer Group 1
                 → Consumer Group 2
                 → Consumer Group 3

特点:

  • 消息按 Topic 分类
  • 不同消费者组之间互不影响
  • 每个消费者组都可以消费完整数据
  • Kafka 更接近发布订阅模式

2. Kafka 基础架构

2.1 核心角色

text 复制代码
Producer → Broker Cluster → Consumer Group
              ↑
           Topic
              ↑
          Partition
              ↑
      Leader / Follower

Producer

消息生产者,负责把数据写入 Kafka。

Broker

一台 Kafka 服务器就是一个 Broker。多个 Broker 组成 Kafka 集群。

Topic

主题,用于对消息进行逻辑分类。

例如:

text 复制代码
user_login
user_click
order_create
payment_success

Partition

分区,是 Kafka 扩展性和并行性的核心。

一个 Topic 可以分成多个 Partition:

text 复制代码
Topic: order
  ├── Partition-0
  ├── Partition-1
  └── Partition-2

每个 Partition 内部是有序的追加日志。

Replica

副本。为了提高可用性,一个 Partition 可以有多个副本。

Leader

每个分区都有一个 Leader 副本。

生产者写数据、消费者读数据,默认都和 Leader 交互。

Follower

Follower 从 Leader 拉取数据进行同步。Leader 挂了以后,Follower 中符合条件的副本可以被选举为新的 Leader。

Consumer

消费者,从 Kafka 中拉取消息。

Consumer Group

消费者组,是 Kafka 并行消费的核心。

同一个消费者组内:

  • 一个 Partition 同一时刻只能被组内一个 Consumer 消费;
  • 一个 Consumer 可以消费多个 Partition;
  • 如果 Consumer 数量大于 Partition 数量,多出来的 Consumer 会空闲。

不同消费者组之间互不影响。


2.2 为什么 Kafka 要设计 Partition?

Partition 有两个核心作用。

1)提高存储扩展性

一个 Topic 的数据量可能非常大,单机存不下。

分区后可以把不同 Partition 分散到不同 Broker 上:

text 复制代码
Partition-0 → Broker-0
Partition-1 → Broker-1
Partition-2 → Broker-2

这样可以横向扩展存储能力。


2)提高读写并行度

生产者可以并行写多个分区。

消费者组内多个 Consumer 可以并行消费多个分区。

text 复制代码
Consumer-0 → Partition-0
Consumer-1 → Partition-1
Consumer-2 → Partition-2

所以 Kafka 的吞吐能力很大程度上来自 Partition。


2.3 Kafka 架构面试回答模板

Kafka 集群由多个 Broker 组成。生产者向 Topic 写入消息,Topic 会被拆分成多个 Partition,每个 Partition 是一个有序日志。为了保证高可用,每个 Partition 可以配置多个副本,其中一个是 Leader,其余是 Follower。生产者和消费者默认只和 Leader 交互,Follower 负责从 Leader 同步数据。消费者通过 Consumer Group 消费数据,同一个组内一个分区只能被一个消费者消费,从而实现组内并行消费;不同消费者组之间互不影响,可以各自独立消费同一份数据。


3. Kafka 快速命令复习

3.1 Topic 操作

查看所有 Topic

bash 复制代码
bin/kafka-topics.sh \
  --bootstrap-server hadoop102:9092 \
  --list

创建 Topic

bash 复制代码
bin/kafka-topics.sh \
  --bootstrap-server hadoop102:9092 \
  --create \
  --topic first \
  --partitions 3 \
  --replication-factor 3

查看 Topic 详情

bash 复制代码
bin/kafka-topics.sh \
  --bootstrap-server hadoop102:9092 \
  --describe \
  --topic first

修改分区数

bash 复制代码
bin/kafka-topics.sh \
  --bootstrap-server hadoop102:9092 \
  --alter \
  --topic first \
  --partitions 6

注意:Kafka 分区数只能增加,不能减少。

删除 Topic

bash 复制代码
bin/kafka-topics.sh \
  --bootstrap-server hadoop102:9092 \
  --delete \
  --topic first

3.2 生产者命令

bash 复制代码
bin/kafka-console-producer.sh \
  --bootstrap-server hadoop102:9092 \
  --topic first

3.3 消费者命令

从当前位置开始消费

bash 复制代码
bin/kafka-console-consumer.sh \
  --bootstrap-server hadoop102:9092 \
  --topic first

从头消费历史数据

bash 复制代码
bin/kafka-console-consumer.sh \
  --bootstrap-server hadoop102:9092 \
  --from-beginning \
  --topic first

指定消费者组

bash 复制代码
bin/kafka-console-consumer.sh \
  --bootstrap-server hadoop102:9092 \
  --topic first \
  --group test-group

4. Kafka Producer 生产者

4.1 Producer 发送消息整体流程

Kafka Producer 内部主要有两个线程:

text 复制代码
main 线程
  ↓
Interceptor 拦截器
  ↓
Serializer 序列化器
  ↓
Partitioner 分区器
  ↓
RecordAccumulator 缓冲区
  ↓
Sender 线程
  ↓
NetworkClient
  ↓
Broker

核心流程:

  1. 用户调用 send() 方法;
  2. Producer 先经过拦截器;
  3. 对 key 和 value 进行序列化;
  4. 根据分区策略选择 Partition;
  5. 消息写入 RecordAccumulator 缓冲区;
  6. Sender 线程从缓冲区拉取满足条件的批次;
  7. 通过 NetworkClient 发送到 Broker;
  8. Broker 根据 acks 配置返回响应;
  9. 成功则清理请求,失败则按 retries 进行重试。

4.2 RecordAccumulator 是什么?

RecordAccumulator 是 Producer 端的发送缓冲区,默认大小是 32MB。

它不是一条消息来一条就立刻发送,而是先按 Topic-Partition 维度缓存成批次。

text 复制代码
RecordAccumulator
  ├── TopicA-Partition0 → ProducerBatch
  ├── TopicA-Partition1 → ProducerBatch
  └── TopicA-Partition2 → ProducerBatch

一个 ProducerBatch 默认大小是 16KB。

发送时机主要由两个参数控制:

text 复制代码
batch.size
linger.ms

batch.size

批次大小,默认 16KB。

数据累计达到 batch.size 后,Sender 线程会发送。

linger.ms

等待时间,默认 0ms。

如果数据迟迟达不到 batch.size,Sender 最多等待 linger.ms,到时间后也会发送。


4.3 异步发送与同步发送

异步发送

java 复制代码
producer.send(record);

特点:

  • 调用后立即返回;
  • 吞吐量高;
  • 发送结果通过回调函数获取;
  • 生产中更常用。

带回调的异步发送

java 复制代码
producer.send(record, (metadata, exception) -> {
    if (exception == null) {
        System.out.println(metadata.topic() + "-" + metadata.partition());
    } else {
        exception.printStackTrace();
    }
});

注意:

消息发送失败后,Kafka Producer 会根据配置自动重试,不要在回调函数里盲目手动重试,否则可能导致重复发送。

同步发送

java 复制代码
producer.send(record).get();

特点:

  • 阻塞等待发送结果;
  • 可靠感更强;
  • 吞吐量低;
  • 适合对顺序或结果强依赖的场景。

4.4 Producer 重要参数

参数 作用 面试重点
bootstrap.servers 初始连接 Broker 地址 不需要写所有 Broker,能连上部分即可获取元数据
key.serializer key 序列化器 必填
value.serializer value 序列化器 必填
buffer.memory RecordAccumulator 总缓冲区大小,默认 32MB 可调大提升吞吐
batch.size 单批次大小,默认 16KB 太小吞吐低,太大延迟高
linger.ms 批次等待时间,默认 0ms 生产建议 5-100ms
acks 应答级别 可靠性核心
retries 重试次数 默认很大
retry.backoff.ms 重试间隔 默认 100ms
enable.idempotence 是否开启幂等 默认 true
max.in.flight.requests.per.connection 单连接未确认请求数,默认 5 影响有序性
compression.type 压缩方式 none/gzip/snappy/lz4/zstd

5. 生产者分区策略

5.1 为什么要分区?

分区的好处:

  1. 提高存储扩展性;
  2. 提高生产和消费并行度;
  3. 可以按 key 保证同类数据进入同一分区;
  4. 可以做负载均衡。

5.2 默认分区策略

Kafka 默认分区器逻辑:

1)如果指定了 partition

直接写入指定分区。

java 复制代码
new ProducerRecord<>("topic", 1, key, value)

2)如果没有指定 partition,但指定了 key

对 key 的 hash 值取模:

text 复制代码
partition = hash(key) % partitionNum

这样相同 key 的消息会进入同一个分区。

适合:

  • 同一个用户的消息按顺序处理;
  • 同一个订单的消息按顺序处理;
  • 同一个设备的数据进入同一个分区。

3)如果既没有 partition,也没有 key

Kafka 使用黏性分区器 Sticky Partition。

特点:

  • 先随机选择一个分区;
  • 尽量一直往这个分区写;
  • 当前批次满了或 linger.ms 到期后,再切换到其他分区;
  • 这样可以提高批量发送效率。

5.3 自定义分区器

如果业务要求特殊路由,可以实现 Partitioner 接口。

典型需求:

text 复制代码
包含 atguigu 的消息 → 0 号分区
其他消息 → 1 号分区

核心步骤:

  1. 实现 Partitioner 接口;
  2. 重写 partition() 方法;
  3. 在 Producer 配置中指定 partitioner.class

5.4 分区策略面试模板

Kafka 生产者选择分区时,首先看 ProducerRecord 是否显式指定了 partition,如果指定了就直接写入该分区;如果没有指定 partition 但有 key,就对 key 的序列化结果做 hash,然后对分区数取模,这样相同 key 可以进入同一个分区;如果既没有 partition 也没有 key,Kafka 会使用 Sticky Partition 黏性分区器,尽量把消息聚合到同一个分区形成批次,等批次满了或者 linger.ms 到期后再切换分区,从而提高吞吐量。


6. Producer 如何提高吞吐量?

6.1 四个核心参数

1)调大 buffer.memory

默认 32MB,可以适当调到 64MB 或更大。

作用:

  • 提高 Producer 端缓存能力;
  • 减少因缓冲区不足导致的阻塞。

2)调大 batch.size

默认 16KB。

作用:

  • 增大批次;
  • 减少网络请求次数;
  • 提高吞吐量。

注意:

  • 太小:频繁网络请求,吞吐量下降;
  • 太大:消息等待时间增加,延迟升高。

3)设置 linger.ms

默认 0ms,表示尽快发送。

生产中可以设置为:

text 复制代码
5ms ~ 100ms

作用:

  • 给 Producer 一点等待时间,让更多消息进入同一批次;
  • 提高批量发送效率。

4)开启压缩 compression.type

常见压缩方式:

text 复制代码
none
gzip
snappy
lz4
zstd

作用:

  • 减少网络传输数据量;
  • 提升吞吐;
  • 代价是增加 CPU 开销。

一般生产中可以考虑:

properties 复制代码
compression.type=snappy

或者:

properties 复制代码
compression.type=lz4

6.2 生产者吞吐量调优模板

properties 复制代码
buffer.memory=67108864
batch.size=32768
linger.ms=10
compression.type=snappy

6.3 面试回答模板

Kafka Producer 提升吞吐量主要从批量、缓存和压缩入手。首先可以适当增大 buffer.memory,避免缓冲区过小导致阻塞;其次增大 batch.size,让更多消息合并成一个请求发送;再次设置合适的 linger.ms,比如 5 到 100ms,让 Producer 等待短暂时间形成批次;最后开启 compression.type,比如 snappy 或 lz4,减少网络传输量。需要注意的是,batch.size 和 linger.ms 过大会增加消息延迟,所以要在吞吐量和延迟之间做权衡。


7. Kafka 数据可靠性:acks、ISR、副本

7.1 acks 三种级别

acks=0

text 复制代码
Producer 发出去就认为成功,不等待 Broker 响应

特点:

  • 吞吐量最高;
  • 可靠性最低;
  • Broker 没收到也不知道;
  • 可能丢数据。

适合:

  • 极端追求性能;
  • 允许丢失的数据;
  • 一般生产环境很少使用。

acks=1

text 复制代码
Leader 收到消息后就返回 ack

特点:

  • 可靠性中等;
  • 吞吐量中等;
  • 如果 Leader 收到后还没同步给 Follower 就挂了,仍然可能丢数据。

适合:

  • 普通日志;
  • 允许少量丢失的场景。

acks=-1 / all

text 复制代码
Leader 和 ISR 中所有副本都收到后才返回 ack

特点:

  • 可靠性最高;
  • 吞吐量相对较低;
  • 仍然需要配合副本数和 min.insync.replicas。

适合:

  • 订单;
  • 支付;
  • 金融;
  • 不能丢的数据。

7.2 ISR 是什么?

ISR 全称:

text 复制代码
In-Sync Replica Set

表示和 Leader 保持同步的副本集合。

注意:

text 复制代码
ISR = Leader + 和 Leader 保持同步的 Follower

如果某个 Follower 长时间没有和 Leader 同步,就会被踢出 ISR。

相关参数:

properties 复制代码
replica.lag.time.max.ms=30000

默认 30s。


7.3 数据完全可靠的条件

想尽量保证数据不丢,需要同时满足:

text 复制代码
acks=-1
副本数 >= 2
min.insync.replicas >= 2

也就是:

properties 复制代码
acks=all
replication.factor=3
min.insync.replicas=2

为什么?

如果:

text 复制代码
replication.factor=1

即使 acks=-1,也只有 Leader 一个副本,本质和 acks=1 差不多。

如果:

text 复制代码
min.insync.replicas=1

ISR 中只剩 Leader 时仍然可以写入,Leader 挂了仍然可能丢数据。


7.4 可靠性面试模板

Kafka 的可靠性主要依赖 acks、ISR 和副本机制。acks=0 表示生产者发送后不等待响应,性能最高但可能丢数据;acks=1 表示 Leader 写入成功就返回,如果 Leader 还没同步给 Follower 就宕机,仍可能丢数据;acks=-1 或 all 表示 Leader 和 ISR 中的副本都写入成功后才返回,可靠性最高。生产中如果要尽量保证不丢数据,通常配置 acks=all、replication.factor 大于等于 3、min.insync.replicas 大于等于 2,这样即使部分副本故障,也能保证至少多个同步副本确认后再返回。


8. Kafka 数据重复、幂等性和事务

8.1 三种消息语义

At Most Once:最多一次

text 复制代码
可能丢,不会重

典型配置:

text 复制代码
acks=0

At Least Once:至少一次

text 复制代码
不丢,但可能重复

典型配置:

text 复制代码
acks=-1
副本数 >= 2
min.insync.replicas >= 2

Exactly Once:精准一次

text 复制代码
既不丢,也不重复

Kafka 0.11 以后通过:

text 复制代码
幂等性 + 事务

支持精准一次语义。


8.2 为什么会重复?

假设 Producer 发送一条消息到 Broker。

Broker 已经写入成功,但返回 ack 时网络异常,Producer 没收到 ack。

Producer 会认为发送失败,然后重试。

于是 Broker 可能收到两条相同消息。

text 复制代码
Producer → Broker 写入成功
Producer ← ack 丢失
Producer 重试
Broker 又写入一次

8.3 幂等性原理

幂等性含义:

Producer 不管向 Broker 发送多少次重复数据,Broker 端只持久化一条。

Kafka 判断重复消息依赖:

text 复制代码
<PID, Partition, Sequence Number>

PID

Producer ID,Producer 启动后由 Kafka 分配。

Partition

消息所属分区。

Sequence Number

每个分区内单调递增的序列号。

如果 Broker 发现相同:

text 复制代码
PID + Partition + SeqNumber

已经写过,就不会重复写入。


8.4 幂等性的限制

幂等性只能保证:

text 复制代码
单 Producer 会话内
单分区
不重复

限制:

  1. Producer 重启后 PID 可能变化;
  2. 跨分区不保证;
  3. 跨会话不保证;
  4. 不能保证多条消息组成的事务原子性。

8.5 开启幂等性

Kafka 3.x 中默认:

properties 复制代码
enable.idempotence=true

8.6 事务机制

事务用于保证多条消息的原子性。

典型场景:

text 复制代码
消费 Topic A
处理数据
写入 Topic B
提交消费 Offset

如果没有事务,可能出现:

  • 消息写入 B 成功,但 Offset 没提交;
  • Offset 提交成功,但消息没写入 B;
  • 造成重复消费或漏消费。

事务可以保证:

text 复制代码
写消息 + 提交 Offset

在一个事务中同时成功或同时失败。


8.7 事务相关 API

java 复制代码
initTransactions();      // 初始化事务
beginTransaction();      // 开启事务
sendOffsetsToTransaction(); // 在事务中提交消费者 Offset
commitTransaction();     // 提交事务
abortTransaction();      // 回滚事务

8.8 Transaction Coordinator

Kafka 事务由 Transaction Coordinator 管理。

事务状态保存在内部 Topic:

text 复制代码
__transaction_state

事务协调器选择规则大致是:

text 复制代码
hash(transactional.id) % __transaction_state 分区数

对应分区 Leader 所在的 Broker 就是这个事务的 Transaction Coordinator。


8.9 幂等性和事务面试模板

Kafka 幂等性用于解决生产者重试导致的单分区重复写入问题。Kafka 会为 Producer 分配 PID,并为每个分区维护递增的 Sequence Number,Broker 根据 PID、Partition 和 Sequence Number 判断消息是否重复,如果重复则只持久化一次。但幂等性只能保证单生产者会话、单分区内不重复。事务是在幂等性的基础上进一步保证多分区、多 Topic 以及消费 Offset 提交的原子性,常用于 consume-process-produce 场景,通过 transactional.id、Transaction Coordinator 和 __transaction_state 维护事务状态。


9. Kafka 数据有序性

9.1 Kafka 是否保证有序?

结论:

text 复制代码
单分区内有序
多分区之间无全局顺序

因为每个 Partition 是追加日志:

text 复制代码
Partition-0: 0 → 1 → 2 → 3
Partition-1: 0 → 1 → 2 → 3

但是 Partition-0 和 Partition-1 之间没有全局顺序。


9.2 如何保证某类消息有序?

把需要有序的数据发到同一个分区。

例如:

text 复制代码
同一个 orderId
同一个 userId
同一个 deviceId

作为 key。

Kafka 会根据 key hash 到同一个分区。


9.3 为什么可能乱序?

Producer 内部有 InFlightRequests。

默认:

properties 复制代码
max.in.flight.requests.per.connection=5

表示一个连接最多可以有 5 个未确认请求。

假设:

text 复制代码
Request1 失败
Request2 成功
Request3 成功
Request1 重试成功

写入顺序就可能变成:

text 复制代码
Request2 → Request3 → Request1

导致乱序。


9.4 保证单分区有序的条件

未开启幂等性

properties 复制代码
max.in.flight.requests.per.connection=1

因为只能一个请求一个请求发,前一个没确认,后一个不能发。

开启幂等性

properties 复制代码
enable.idempotence=true
max.in.flight.requests.per.connection <= 5

Kafka 服务端会缓存最近 5 个 request 的元数据,并保证它们有序。


9.5 有序性面试模板

Kafka 只能保证单分区内有序,不能保证多分区全局有序。如果业务要求某类消息有序,需要把这类消息通过相同 key 发送到同一个分区。乱序通常和生产者重试以及 max.in.flight.requests.per.connection 有关,如果未开启幂等性,要严格保证单分区有序,需要把 max.in.flight.requests.per.connection 设置为 1;如果开启了幂等性,Kafka 可以在 max.in.flight 小于等于 5 的情况下保证单分区有序。


10. Kafka Broker 工作机制

10.1 Broker 启动后做了什么?

Broker 启动后会向元数据管理组件注册自己。

早期 Kafka 使用 Zookeeper。

Zookeeper 中记录:

text 复制代码
/kafka/brokers/ids
/kafka/brokers/topics/.../state
/kafka/controller

主要信息包括:

  • 当前有哪些 Broker;
  • 每个 Partition 的 Leader 是谁;
  • ISR 中有哪些副本;
  • Controller 是谁。

Kafka 2.8 以后也可以配置不依赖 Zookeeper,使用 KRaft 模式。


10.2 Controller 是什么?

Controller 是 Kafka 集群中的一个 Broker 角色。

作用:

  • 监听 Broker 上下线;
  • 管理 Partition Leader 选举;
  • 维护 Leader 和 ISR 信息;
  • 触发分区副本状态变更;
  • 负责集群元数据管理。

10.3 Leader 选举流程

当某个 Partition 的 Leader 所在 Broker 挂掉:

  1. Controller 监听到 Broker 下线;
  2. Controller 获取该分区 ISR;
  3. 从 ISR 中选择新的 Leader;
  4. 选举规则一般是在 ISR 存活副本中,按照 AR 顺序优先选择;
  5. 更新 Leader 和 ISR 信息;
  6. 其他 Broker 同步新的元数据。

相关概念:

text 复制代码
AR = Assigned Replicas,分区的所有副本集合
ISR = In-Sync Replicas,和 Leader 保持同步的副本集合

Leader 选举原则:

text 复制代码
优先从 ISR 中选
再按 AR 顺序选择靠前的副本

10.4 Broker 面试模板

Kafka 集群中会有一个 Controller,负责监听 Broker 上下线和 Partition Leader 选举。每个分区都有 AR 和 ISR,AR 是该分区所有副本,ISR 是和 Leader 保持同步的副本集合。当 Leader 所在 Broker 宕机后,Controller 会感知到节点变化,然后从 ISR 中选择新的 Leader,通常按照 AR 中副本顺序选择优先级较高且仍存活的副本,并更新集群元数据。这样 Kafka 可以在 Broker 故障后继续提供服务。


11. Kafka 副本机制、HW、LEO

11.1 副本机制

每个 Partition 可以有多个副本:

text 复制代码
Partition-0
  ├── Leader
  ├── Follower-1
  └── Follower-2

生产者写 Leader。

消费者读 Leader。

Follower 从 Leader 拉取数据。


11.2 LEO 是什么?

LEO:

text 复制代码
Log End Offset

表示某个副本日志的末端位置。

可以理解为:

text 复制代码
最新一条消息 offset + 1

11.3 HW 是什么?

HW:

text 复制代码
High Watermark

高水位。

表示消费者可见的最大 Offset。

一般取 ISR 中所有副本 LEO 的最小值。

text 复制代码
HW = min(LEO of ISR)

只有 HW 之前的数据才对消费者可见。


11.4 Follower 故障如何处理?

  1. Follower 落后太多或长时间不同步;
  2. 被踢出 ISR;
  3. Leader 和其他 Follower 继续工作;
  4. 故障 Follower 恢复后,先读取本地 HW;
  5. 截断 HW 之后的不一致数据;
  6. 从 Leader 重新同步;
  7. 追上 Leader 后重新加入 ISR。

11.5 Leader 故障如何处理?

  1. Leader 挂掉;
  2. Controller 从 ISR 中选新的 Leader;
  3. 其他 Follower 会截断高于 HW 的日志;
  4. 然后从新 Leader 同步数据;
  5. 保证副本之间一致。

注意:

HW/LEO 机制可以保证副本之间一致,但不能单独保证业务层面绝对不丢或不重复。是否丢失还要看 acks、副本数、min.insync.replicas 等配置。


12. Kafka 存储机制

12.1 Topic、Partition、Segment 的关系

text 复制代码
Topic
  ├── Partition-0
  │     ├── Segment-0
  │     │     ├── .log
  │     │     ├── .index
  │     │     └── .timeindex
  │     └── Segment-1
  └── Partition-1
        └── Segment-0

Topic

逻辑概念。

Partition

物理概念,一个 Partition 对应一个日志目录。

Segment

Partition 内部再切分成多个 Segment 文件。

默认:

properties 复制代码
log.segment.bytes=1GB

12.2 Segment 里有哪些文件?

.log

真正存储消息数据。

.index

偏移量索引文件。

用于根据 offset 快速定位消息在 .log 文件中的物理位置。

.timeindex

时间戳索引文件。

用于根据时间查找消息位置。


12.3 为什么 Kafka 使用 Segment?

如果一个 Partition 只有一个巨大文件:

  • 删除历史数据困难;
  • 查找效率低;
  • 文件过大不好管理。

拆成 Segment 后:

  • 可以按 Segment 删除过期数据;
  • 可以分段建立索引;
  • 可以提高查找和维护效率。

12.4 稀疏索引

Kafka 的索引不是每条消息都建索引,而是稀疏索引。

默认每写入一定大小数据,记录一个索引。

相关参数:

properties 复制代码
log.index.interval.bytes=4096

也就是大约每写入 4KB 数据记录一次索引。

好处:

  • 索引文件小;
  • 查询速度较快;
  • 内存占用低。

查找时:

  1. 先在 .index 里找到不大于目标 offset 的最近索引;
  2. 定位到 .log 文件的大概位置;
  3. 顺序扫描少量数据找到目标消息。

13. Kafka 日志清理策略

Kafka 不是消费完就删除消息,而是按保留策略删除或压缩。

13.1 delete 删除策略

默认策略:

properties 复制代码
log.cleanup.policy=delete

常见参数:

properties 复制代码
log.retention.hours=168
log.retention.minutes
log.retention.ms
log.retention.bytes
log.retention.check.interval.ms=300000

默认保留时间通常是 7 天。

删除策略可以按:

  • 时间;
  • 总大小。

删除的是旧的 Segment 文件。


13.2 compact 压缩策略

配置:

properties 复制代码
log.cleanup.policy=compact

compact 不是压缩体积的 gzip 那种压缩,而是日志压缩/合并。

核心规则:

text 复制代码
相同 key 的多条消息,只保留最后一个 value

例如:

text 复制代码
offset: 0  key=K1 value=V1
offset: 3  key=K1 value=V4

压缩后只保留:

text 复制代码
key=K1 value=V4

适合场景:

  • 用户最新资料;
  • 配置信息;
  • 状态快照;
  • 只关心某个 key 的最新值。

14. Kafka 为什么快?

这是高频面试题。

Kafka 高吞吐主要来自以下几点。

14.1 分区并行

Topic 拆成多个 Partition。

不同 Partition 可以分布在不同 Broker 上。

生产者和消费者都可以并行处理。


14.2 顺序写磁盘

Kafka 写数据是追加写:

text 复制代码
append-only log

顺序写磁盘比随机写快得多。

磁盘顺序写可以接近内存级吞吐,而随机写会受磁头寻址影响很大。


14.3 PageCache

Kafka 重度依赖操作系统 PageCache。

写入时:

text 复制代码
Kafka → PageCache → 异步刷盘

读取时:

text 复制代码
先查 PageCache
没有再读磁盘

好处:

  • 利用操作系统缓存;
  • 减少 JVM 堆内存压力;
  • 避免频繁 GC;
  • 热数据读取非常快。

14.4 零拷贝

普通拷贝流程大致是:

text 复制代码
磁盘 → 内核态 PageCache → 用户态 Kafka 应用 → 内核态 Socket Buffer → 网卡

Kafka 使用零拷贝后:

text 复制代码
磁盘/PageCache → Socket Buffer → 网卡

数据不需要经过 Kafka 用户态应用层反复拷贝。

好处:

  • 减少 CPU 拷贝;
  • 减少上下文切换;
  • 提高网络发送效率。

14.5 批量发送

Producer 端通过:

text 复制代码
RecordAccumulator
ProducerBatch
batch.size
linger.ms

把多条消息合并成批次发送,减少网络请求次数。


14.6 压缩

Producer 端可以压缩消息:

text 复制代码
snappy / lz4 / zstd / gzip

减少网络 IO 和磁盘 IO。


14.7 稀疏索引

Kafka 通过稀疏索引快速定位消息,不需要全量扫描日志。


14.8 面试模板

Kafka 快主要有几个原因。第一,它通过 Partition 实现并行读写,提升集群扩展能力;第二,Kafka 写消息是追加写日志,属于顺序写磁盘,性能远高于随机写;第三,Kafka 充分利用操作系统 PageCache,读写都可以走页缓存,减少磁盘 IO 和 JVM GC 压力;第四,Kafka 消费数据时使用零拷贝技术,数据可以从 PageCache 直接发送到网卡,减少用户态和内核态之间的数据拷贝;第五,Producer 端通过 batch.size 和 linger.ms 做批量发送,并支持压缩,进一步提高吞吐量。


15. Kafka Consumer 消费者

15.1 消费者基本流程

text 复制代码
KafkaConsumer 创建
  ↓
订阅 Topic
  ↓
poll 拉取消息
  ↓
反序列化
  ↓
业务处理
  ↓
提交 Offset

消费者不是 Broker 主动推送,而是 Consumer 主动拉取。


15.2 Consumer 重要参数

参数 作用 默认值 / 重点
bootstrap.servers 连接 Kafka 集群 host:port
key.deserializer key 反序列化器 必填
value.deserializer value 反序列化器 必填
group.id 消费者组 ID 必填
enable.auto.commit 是否自动提交 Offset 默认 true
auto.commit.interval.ms 自动提交间隔 默认 5s
auto.offset.reset 无 Offset 时从哪里消费 latest / earliest / none
heartbeat.interval.ms 心跳间隔 默认 3s
session.timeout.ms 会话超时时间 默认 45s
max.poll.interval.ms 两次 poll 最大间隔 默认 5min
fetch.min.bytes 每批最小拉取字节数 默认 1B
fetch.max.wait.ms 拉取等待时间 默认 500ms
fetch.max.bytes 每批最大拉取字节数 默认 50MB
max.poll.records 每次 poll 最大记录数 默认 500
partition.assignment.strategy 分区分配策略 Range + CooperativeSticky

15.3 Consumer Group 是什么?

Consumer Group 是 Kafka 实现并行消费的核心。

规则:

text 复制代码
同一个消费者组内:
一个 Partition 只能被一个 Consumer 消费。

不同消费者组之间:
互不影响,可以重复消费同一 Topic。

例如:

text 复制代码
TopicA 有 3 个分区
Consumer Group 有 3 个消费者

C0 → P0
C1 → P1
C2 → P2

如果消费者数量超过分区数量:

text 复制代码
TopicA 有 3 个分区
Consumer Group 有 5 个消费者

那么有 2 个消费者会空闲。


15.4 Coordinator 是什么?

Coordinator 用来管理消费者组。

Coordinator 的选择规则:

text 复制代码
hash(group.id) % __consumer_offsets 分区数

找到 __consumer_offsets 对应分区的 Leader 所在 Broker,该 Broker 就是这个消费者组的 Coordinator。

Coordinator 负责:

  • 管理消费者组成员;
  • 接收 JoinGroup 请求;
  • 触发 Rebalance;
  • 下发分区分配方案;
  • 接收 Offset 提交;
  • 维护心跳。

15.5 消费者组初始化流程

  1. 每个 Consumer 向 Coordinator 发送 JoinGroup 请求;
  2. Coordinator 选出一个 Consumer 作为 Consumer Leader;
  3. Coordinator 把 Topic 分区信息发给 Consumer Leader;
  4. Consumer Leader 制定分区分配方案;
  5. Consumer Leader 把方案发给 Coordinator;
  6. Coordinator 下发方案给每个 Consumer;
  7. Consumer 和 Coordinator 保持心跳。

15.6 Rebalance 是什么?

Rebalance 是消费者组重新分配分区。

触发场景:

  1. 消费者加入消费者组;
  2. 消费者退出消费者组;
  3. 消费者宕机;
  4. Topic 分区数变化;
  5. 消费者心跳超时;
  6. 消费者处理消息太慢,超过 max.poll.interval.ms

相关参数:

properties 复制代码
heartbeat.interval.ms=3000
session.timeout.ms=45000
max.poll.interval.ms=300000

15.7 Rebalance 的问题

Rebalance 期间,消费者组可能暂停消费。

问题:

  • 消费暂停;
  • 延迟升高;
  • 重复消费风险增加;
  • 分区频繁迁移;
  • 大消费者组抖动明显。

优化方向:

  • 避免消费者频繁上下线;
  • 控制单次消息处理时间;
  • 合理设置 max.poll.records
  • 处理逻辑耗时较长时,适当调大 max.poll.interval.ms
  • 使用更平滑的 CooperativeSticky 策略。

16. 分区分配策略

Kafka 常见分区分配策略:

text 复制代码
Range
RoundRobin
Sticky
CooperativeSticky

配置参数:

properties 复制代码
partition.assignment.strategy

16.1 Range 策略

Range 是按每个 Topic 单独分配。

假设:

text 复制代码
7 个分区:P0 P1 P2 P3 P4 P5 P6
3 个消费者:C0 C1 C2

分配结果:

text 复制代码
C0: P0 P1 P2
C1: P3 P4
C2: P5 P6

规则:

text 复制代码
分区数 / 消费者数
除不尽时,前面的消费者多分一个

缺点:

如果订阅很多 Topic,每个 Topic 都让 C0 多消费一个分区,容易造成 C0 负载过高。


16.2 RoundRobin 策略

RoundRobin 是把所有 Topic 的所有 Partition 和所有 Consumer 排序,然后轮询分配。

例如:

text 复制代码
C0: P0 P3 P6
C1: P1 P4
C2: P2 P5

优点:

  • 分配更均匀。

缺点:

  • 如果消费者订阅的 Topic 不完全一致,可能出现复杂情况。

16.3 Sticky 策略

Sticky 目标:

  1. 分配尽量均匀;
  2. Rebalance 后尽量保持原来的分配不变。

好处:

  • 减少分区迁移;
  • 降低 Rebalance 成本。

16.4 CooperativeSticky 策略

CooperativeSticky 是增量式再平衡。

传统 Rebalance 可能一次性撤销所有分区,再重新分配。

CooperativeSticky 尽量只迁移必要的分区。

好处:

  • 减少暂停时间;
  • 降低消费者组抖动;
  • Kafka 新版本默认策略之一。

16.5 分区分配策略面试模板

Kafka 消费者组的分区分配策略主要有 Range、RoundRobin、Sticky 和 CooperativeSticky。Range 是按每个 Topic 单独分配,容易在多 Topic 场景下让前面的消费者多消费分区,造成倾斜;RoundRobin 是把所有分区和消费者排序后轮询分配,整体更均匀;Sticky 在保证相对均匀的同时尽量保持上一次分配结果,减少 Rebalance 后的分区迁移;CooperativeSticky 是增量式再平衡,避免一次性撤销所有分区,可以降低 Rebalance 对消费的影响。


17. Offset 机制

17.1 Offset 是什么?

Offset 是消费者消费到某个 Partition 的位置。

每个 Partition 内消息都有自己的 Offset:

text 复制代码
Partition-0: 0 1 2 3 4 5
Partition-1: 0 1 2 3

消费者提交 Offset 后,下次就可以从提交的位置继续消费。


17.2 Offset 存在哪里?

Kafka 0.9 以前:

text 复制代码
Zookeeper

Kafka 0.9 以后:

text 复制代码
__consumer_offsets

__consumer_offsets 是 Kafka 内部 Topic。

默认有 50 个分区。


17.3 自动提交 Offset

配置:

properties 复制代码
enable.auto.commit=true
auto.commit.interval.ms=5000

特点:

  • 使用简单;
  • 默认每 5s 提交一次;
  • 但提交时机不好控制;
  • 可能导致重复消费或漏消费。

17.4 手动提交 Offset

关闭自动提交:

properties 复制代码
enable.auto.commit=false

commitSync 同步提交

java 复制代码
consumer.commitSync();

特点:

  • 阻塞等待提交成功;
  • 失败会重试;
  • 更可靠;
  • 性能较低。

commitAsync 异步提交

java 复制代码
consumer.commitAsync();

特点:

  • 不阻塞;
  • 性能高;
  • 没有自动失败重试;
  • 可能提交失败。

17.5 重复消费场景

自动提交时:

text 复制代码
1. Consumer 拉取消息
2. 业务处理了一部分
3. 还没到自动提交时间
4. Consumer 宕机
5. 重启后从上次已提交 Offset 开始消费
6. 已处理但未提交的消息会重复消费

结论:

text 复制代码
业务处理成功了,但 Offset 没提交 → 重复消费

17.6 漏消费场景

手动提交时,如果先提交 Offset,再处理数据:

text 复制代码
1. Consumer 拉取消息
2. 先提交 Offset
3. 数据还没处理完
4. Consumer 宕机
5. 重启后从新 Offset 开始
6. 已提交但未处理的数据丢失

结论:

text 复制代码
Offset 提交了,但业务没处理成功 → 漏消费

17.7 正确处理建议

一般建议:

text 复制代码
先处理业务
处理成功后再提交 Offset

但这样仍可能重复消费。

所以业务端要做幂等:

  • 数据库唯一键;
  • 去重表;
  • 业务唯一 ID;
  • Redis 去重;
  • 状态机校验;
  • 幂等写入。

17.8 auto.offset.reset

当 Kafka 中没有初始 Offset 或 Offset 不存在时使用。

properties 复制代码
auto.offset.reset=latest

latest

从最新位置开始消费。

默认值。

earliest

从最早位置开始消费。

none

没有 Offset 就抛异常。


17.9 指定 Offset 消费

java 复制代码
consumer.seek(topicPartition, 1000);

表示从指定分区的 offset=1000 开始消费。


17.10 指定时间消费

可以通过:

java 复制代码
consumer.offsetsForTimes(timestampToSearch);

找到某个时间点对应的 Offset,然后 seek 到该位置消费。


17.11 Offset 面试模板

Kafka 的 Offset 表示消费者在某个 Partition 上消费到的位置。Kafka 0.9 以后,消费者 Offset 默认保存在内部 Topic __consumer_offsets 中。Offset 可以自动提交,也可以手动提交。自动提交简单,但提交时机不可控,可能导致重复消费;手动提交可以在业务处理完成后提交,更可控,但如果提交和业务处理顺序不当,也可能出现漏消费。实际生产中通常关闭自动提交,业务处理成功后手动提交 Offset,同时在业务侧通过唯一键、去重表或幂等逻辑解决重复消费问题。


18. Kafka 如何保证精准一次?

这是 Kafka 面试最容易被追问的题。

18.1 生产端精准一次

生产端要避免丢失和重复,需要:

properties 复制代码
acks=all
enable.idempotence=true
retries>0
replication.factor>=3
min.insync.replicas>=2

作用:

  • acks=all 保证 ISR 副本确认;
  • 幂等性避免 Producer 重试导致重复写;
  • 副本和 ISR 配置降低 Leader 故障丢失风险。

18.2 Kafka 内部流转精准一次

对于:

text 复制代码
consume from topic A
process
produce to topic B
commit offset

需要使用事务:

text 复制代码
生产消息 + 提交 Offset

放入同一个事务。

这样可以保证:

  • 消息写入成功,Offset 也提交;
  • 消息写入失败,Offset 不提交;
  • 避免消费-生产链路不一致。

消费者还需要配置:

properties 复制代码
isolation.level=read_committed

只读取已提交事务的数据。


18.3 Kafka 到外部系统是否天然精准一次?

不一定。

例如:

text 复制代码
Kafka → Consumer → MySQL

Kafka 只能管理 Kafka 内部事务,不能天然保证 MySQL 写入和 Kafka Offset 提交的全局事务一致。

解决方式:

  • 业务幂等;
  • MySQL 唯一键;
  • 本地事务表;
  • Outbox 模式;
  • 先写业务表和消费记录表,再提交 Offset;
  • 使用 Flink Checkpoint + 两阶段提交 Sink。

18.4 精准一次面试模板

Kafka 的精准一次要分场景看。生产者写 Kafka 时,可以通过 acks=all、幂等性、合理副本和 min.insync.replicas 配置,保证不丢且避免重试重复。对于 Kafka 内部的 consume-process-produce 场景,需要使用事务,把生产结果和消费 Offset 提交放到同一个事务中,并让消费者使用 read_committed 读取已提交数据。但如果消费者把数据写入 MySQL、Redis 等外部系统,Kafka 本身不能直接保证端到端精准一次,通常还需要业务幂等、唯一键、去重表或外部系统事务配合。


19. Kafka 消息积压如何处理?

19.1 什么是消息积压?

生产速度大于消费速度,Lag 越来越大。

text 复制代码
Producer 写入速度 > Consumer 消费速度

19.2 排查方向

1)看生产端是否突然流量暴增

例如:

  • 活动流量;
  • 日志暴增;
  • 上游异常重试;
  • 突然新增埋点。

2)看消费者处理是否变慢

可能原因:

  • 数据库慢;
  • RPC 慢;
  • 业务逻辑耗时;
  • 单条处理太重;
  • 下游限流;
  • 消费线程阻塞。

3)看分区数是否不足

消费者组并行度上限由分区数决定。

text 复制代码
最大有效消费者数 = Partition 数

如果只有 3 个分区,启动 10 个 Consumer,也只有 3 个能真正消费。

4)看单次拉取参数

重点参数:

properties 复制代码
fetch.max.bytes
max.poll.records

5)看 Rebalance 是否频繁

频繁 Rebalance 会导致消费暂停。


19.3 解决方案

短期应急

  1. 增加消费者实例;
  2. 前提是分区数足够;
  3. 提高 max.poll.records
  4. 提高 fetch.max.bytes;
  5. 批量处理消息;
  6. 降低单条业务处理耗时;
  7. 临时扩容下游服务。

长期优化

  1. 增加 Topic 分区数;
  2. 优化数据库写入,改成批量写;
  3. 消费逻辑异步化;
  4. 使用线程池处理业务;
  5. 拆分慢消费者;
  6. 做限流和降级;
  7. 建立 Lag 监控和告警。

19.4 消息积压面试模板

Kafka 消息积压本质是生产速度大于消费速度。排查时我会先看 Lag 是否持续增长,再看生产端是否流量突增、消费者是否处理变慢、下游数据库或 RPC 是否成为瓶颈,以及是否发生频繁 Rebalance。解决时可以先增加消费者实例,但前提是分区数足够,因为一个消费者组的最大并行度受 Partition 数限制;然后可以调大 fetch.max.bytes 和 max.poll.records,提高单次拉取量;同时优化业务处理逻辑,比如批量写数据库、异步处理、扩容下游。如果是长期问题,需要增加分区数、拆分 Topic 或优化整体消费链路。


20. Kafka Broker 核心参数

参数 作用 建议
broker.id Broker 唯一 ID 集群内不能重复
log.dirs 日志数据目录 可配置多个磁盘
num.network.threads 网络线程数 根据 CPU 调整
num.io.threads IO 线程数 根据磁盘和 CPU 调整
num.replica.fetchers 副本拉取线程数 副本同步相关
replica.lag.time.max.ms Follower 超时踢出 ISR 时间 默认 30s
log.segment.bytes Segment 文件大小 默认 1GB
log.index.interval.bytes 稀疏索引间隔 默认 4KB
log.retention.hours 日志保留时间 默认 7 天
log.retention.bytes 日志保留大小 -1 表示不限
log.cleanup.policy 日志清理策略 delete / compact
auto.create.topics.enable 是否自动创建 Topic 生产建议 false
auto.leader.rebalance.enable 是否自动 Leader 平衡 生产中可考虑 false
log.flush.interval.messages 强制刷盘消息条数 一般不建议改
log.flush.interval.ms 强制刷盘时间 一般不建议改

21. 自动创建 Topic 为什么生产环境建议关闭?

默认:

properties 复制代码
auto.create.topics.enable=true

如果生产者向一个不存在的 Topic 发消息,Kafka 会自动创建 Topic。

问题:

  • Topic 名字写错也会创建;
  • 默认分区数和副本数可能不符合生产要求;
  • 增加运维管理难度;
  • 可能导致线上出现大量脏 Topic。

生产建议:

properties 复制代码
auto.create.topics.enable=false

所有 Topic 提前规划和创建。


22. 分区数如何设计?

22.1 分区数影响什么?

分区数影响:

  • 生产并行度;
  • 消费并行度;
  • 文件句柄数量;
  • Leader 数量;
  • Rebalance 成本;
  • Controller 管理压力。

22.2 分区数不是越多越好

分区过少:

  • 吞吐上不去;
  • 消费并行度不足。

分区过多:

  • 文件数量多;
  • 元数据压力大;
  • Rebalance 慢;
  • Leader 选举成本高;
  • 小文件和索引变多。

22.3 面试回答模板

Kafka 分区数要结合吞吐量、消费者并行度和集群规模综合评估。分区数太少会限制生产和消费并行度,导致吞吐上不去;但分区数也不是越多越好,过多会增加文件句柄、元数据、Leader 选举和 Rebalance 成本。一般会根据峰值吞吐、单分区吞吐能力、消费者实例数和未来扩展预留来设计,同时保证消费者组内有效消费者数不超过分区数太多,避免资源浪费。


23. 生产环境硬件配置估算

23.1 估算示例

假设:

text 复制代码
100 万日活
每人每天 100 条日志
每天总日志 = 1 亿条
每条日志约 1KB

每天数据量:

text 复制代码
1 亿 * 1KB ≈ 100GB

平均每秒:

text 复制代码
1 亿 / 24 / 3600 ≈ 1150 条/s
1150 * 1KB ≈ 1MB/s

高峰按 20 倍估算:

text 复制代码
20MB/s

23.2 服务器台数估算

课件公式:

text 复制代码
服务器台数 = 2 * (生产者峰值生产速率 * 副本数 / 100) + 1

例如:

text 复制代码
2 * (20MB/s * 2 / 100) + 1 = 3 台

23.3 磁盘选择

Kafka 主要是顺序写。

机械硬盘顺序写性能也不错,所以可以使用普通机械硬盘。

容量估算:

text 复制代码
每天数据量 * 副本数 * 保留天数 / 磁盘使用率

例如:

text 复制代码
100GB * 2 * 3 / 0.7 ≈ 1TB

23.4 内存选择

Kafka 内存主要分为:

text 复制代码
JVM 堆内存 + 操作系统 PageCache

堆内存

建议每个节点:

text 复制代码
10GB ~ 15GB

PageCache

Kafka 依赖 PageCache。

估算方式:

text 复制代码
每个节点 PageCache ≈ 分区数 * 1GB * 25% / 节点数

23.5 CPU 选择

课件建议:

text 复制代码
num.io.threads 占总核数约 50%
num.network.threads 占总核数约 50% 的 2/3
num.replica.fetchers 占总核数约 50% 的 1/3

中型场景可考虑 32 core 级别。


23.6 网络选择

网络带宽至少覆盖峰值吞吐。

例如峰值:

text 复制代码
20MB/s

千兆网卡理论约:

text 复制代码
1000Mbps / 8 = 125MB/s

通常够用。


24. Kafka 总体吞吐量优化

24.1 生产者侧

properties 复制代码
buffer.memory=64MB
batch.size=32KB 或更大
linger.ms=5~100ms
compression.type=snappy/lz4/zstd
acks 根据可靠性要求配置

24.2 Broker 侧

优化点:

  • 合理分区数;
  • 合理副本数;
  • 多磁盘目录;
  • 保证 PageCache;
  • 顺序写;
  • 避免频繁刷盘;
  • 监控磁盘 IO;
  • 关闭非预期自动创建 Topic;
  • 合理设置日志保留时间。

24.3 消费者侧

properties 复制代码
fetch.max.bytes=50MB 或更大
max.poll.records=500 或更大
fetch.min.bytes 适当调大
fetch.max.wait.ms 适当调大

优化方向:

  • 增加分区;
  • 增加消费者;
  • 批量处理;
  • 提升下游处理能力;
  • 减少 Rebalance;
  • 优化业务逻辑。

24.4 总体吞吐量面试模板

Kafka 吞吐量优化要从生产者、Broker 和消费者三侧看。生产者侧可以增大 batch.size、buffer.memory,设置合适的 linger.ms,并开启压缩来减少网络 IO;Broker 侧要合理设计分区和副本,利用顺序写、PageCache 和零拷贝,同时避免频繁刷盘;消费者侧可以增加分区和消费者实例,调大 fetch.max.bytes 和 max.poll.records,并优化下游处理能力。最终要根据业务在吞吐、延迟和可靠性之间做权衡。


25. Kafka 外部系统集成

25.1 Flume 集成 Kafka

Flume 可以作为 Kafka 生产者,也可以作为 Kafka 消费者。

Flume → Kafka

典型链路:

text 复制代码
TailDirSource → MemoryChannel → KafkaSink → Kafka Topic

适合:

  • 采集日志文件;
  • 采集 app.log;
  • 写入 Kafka 做实时处理。

关键配置:

properties 复制代码
a1.sources.r1.type = TAILDIR
a1.sources.r1.filegroups.f1 = /opt/module/applog/app.*
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.bootstrap.servers = hadoop102:9092,hadoop103:9092,hadoop104:9092
a1.sinks.k1.kafka.topic = first
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
a1.sinks.k1.kafka.producer.linger.ms = 1

Kafka → Flume

典型链路:

text 复制代码
KafkaSource → MemoryChannel → Logger/FileSink

关键配置:

properties 复制代码
a1.sources.r1.type = org.apache.flume.source.kafka.KafkaSource
a1.sources.r1.kafka.bootstrap.servers = hadoop102:9092
a1.sources.r1.kafka.topics = first
a1.sources.r1.kafka.consumer.group.id = custom.g.id

Flink 可以作为 Kafka 生产者,也可以作为 Kafka 消费者。

text 复制代码
DataStream → Kafka Producer → Topic

核心:

java 复制代码
stream.addSink(kafkaProducer);

text 复制代码
Kafka Topic → FlinkKafkaConsumer → DataStream

核心:

java 复制代码
env.addSource(kafkaConsumer).print();

25.3 SpringBoot 集成 Kafka

SpringBoot 中常用:

text 复制代码
KafkaTemplate
@KafkaListener

生产者

java 复制代码
@Autowired
KafkaTemplate<String, String> kafkaTemplate;

kafkaTemplate.send("first", msg);

消费者

java 复制代码
@KafkaListener(topics = "first", groupId = "atguigu")
public void consume(String msg) {
    System.out.println(msg);
}

26. Kafka 常见面试题模板

26.1 Kafka 是什么?

Kafka 是一个分布式事件流平台,也可以理解为高吞吐的分布式消息队列。它通过 Topic 对消息分类,通过 Partition 实现水平扩展和并行读写,通过副本机制保证高可用,通过消费者组实现并行消费。Kafka 常用于日志采集、削峰填谷、系统解耦、异步通信和实时流处理。


26.2 Kafka 为什么吞吐量高?

Kafka 吞吐量高主要因为它使用 Partition 实现并行读写,写入采用追加日志的顺序写磁盘方式,读写大量依赖操作系统 PageCache,消费数据时使用零拷贝减少用户态和内核态之间的数据拷贝,同时 Producer 支持批量发送和压缩,减少网络 IO 和磁盘 IO,因此整体吞吐非常高。


26.3 Kafka 如何保证消息不丢?

Kafka 防止消息丢失要从生产者、Broker 和消费者三端考虑。生产者端使用 acks=all、retries 和幂等性;Broker 端配置多副本,并设置 min.insync.replicas>=2,保证至少多个同步副本写入成功;消费者端关闭自动提交 Offset,在业务处理成功后再手动提交 Offset。这样可以尽量保证消息不丢,但业务侧仍要考虑重复消费和幂等处理。


26.4 Kafka 会不会重复消费?

会。Kafka 默认更容易做到至少一次语义,也就是不丢但可能重复。比如消费者处理完消息但还没提交 Offset 就宕机,重启后会从上次提交的 Offset 继续消费,导致重复消费。解决方式是业务侧做幂等,比如使用唯一键、去重表、Redis 去重或状态机判断。


26.5 Kafka 如何保证顺序消费?

Kafka 只能保证单分区内有序,不能保证多分区全局有序。如果业务要求某类消息有序,需要用相同 key 把这些消息写入同一个分区。生产者侧如果未开启幂等性,还需要把 max.in.flight.requests.per.connection 设置为 1,避免重试导致乱序;如果开启了幂等性,该值小于等于 5 时 Kafka 可以保证单分区有序。


26.6 Kafka 的 acks 有哪些?

acks=0 表示生产者发送后不等待 Broker 响应,吞吐最高但可能丢数据;acks=1 表示 Leader 写入成功就返回,如果 Leader 没来得及同步给 Follower 就宕机,仍可能丢数据;acks=all 或 -1 表示 Leader 和 ISR 中副本都确认后才返回,可靠性最高但吞吐较低。生产中重要数据通常使用 acks=all,并配合 replication.factor>=3、min.insync.replicas>=2。


26.7 ISR 是什么?

ISR 是 In-Sync Replica Set,表示和 Leader 保持同步的副本集合,包括 Leader 本身和同步正常的 Follower。如果某个 Follower 长时间没有同步数据或没有和 Leader 通信,就会被踢出 ISR。acks=all 时,Kafka 只需要等待 ISR 中副本确认,而不是等待所有 AR 副本,这样可以在可靠性和可用性之间做平衡。


26.8 Kafka 幂等性怎么实现?

Kafka 幂等性通过 PID、Partition 和 Sequence Number 实现。Producer 启动后会获得一个 PID,每个分区上的消息都有递增的 Sequence Number。Broker 收到消息后,如果发现相同 PID、Partition 和 Sequence Number 的消息已经写入过,就不会重复持久化。幂等性解决的是生产者重试导致的单分区重复写问题,但只能保证单生产者会话、单分区内不重复。


26.9 Kafka 事务解决什么问题?

Kafka 事务解决的是多条消息、多分区、多 Topic 以及消费 Offset 提交的原子性问题。典型场景是消费一个 Topic,处理后写入另一个 Topic,并提交 Offset。通过事务,可以把生产结果和 Offset 提交放到同一个事务中,要么都成功,要么都失败,避免写入结果和 Offset 状态不一致。


26.10 Kafka 消费者组是什么?

消费者组是 Kafka 实现并行消费的机制。同一个消费者组内,一个分区同一时刻只能由一个消费者消费,一个消费者可以消费多个分区;不同消费者组之间互不影响,可以各自独立消费同一个 Topic 的完整数据。因此消费者组既可以实现负载均衡,也可以支持多套业务系统独立订阅同一份数据。


26.11 Rebalance 是什么?

Rebalance 是消费者组内分区重新分配的过程。当消费者加入、退出、宕机、Topic 分区数变化、心跳超时或消息处理超过 max.poll.interval.ms 时,都会触发 Rebalance。Rebalance 会导致消费短暂停顿,并可能带来重复消费,因此生产中要避免消费者频繁上下线,控制单次处理时长,并合理配置心跳和 poll 参数。


26.12 Kafka Offset 是什么?存在哪里?

Offset 是消费者在某个 Partition 上消费到的位置。Kafka 0.9 以前 Offset 存在 Zookeeper 中,0.9 以后默认存储在 Kafka 内部 Topic __consumer_offsets 中。消费者可以自动提交 Offset,也可以手动提交。生产中通常关闭自动提交,在业务处理成功后手动提交 Offset,以减少漏消费风险。


26.13 自动提交和手动提交 Offset 区别?

自动提交由 Kafka Consumer 按时间周期提交,默认每 5 秒提交一次,使用简单但提交时机不可控,可能导致重复消费或漏消费。手动提交由业务代码控制,可以在业务处理成功后调用 commitSync 或 commitAsync 提交 Offset。commitSync 更可靠但会阻塞,commitAsync 性能更好但失败不会自动重试。


26.14 Kafka 如何处理消息积压?

Kafka 消息积压说明生产速度大于消费速度。排查时先看生产流量是否突增、消费者处理是否变慢、下游系统是否瓶颈、分区数是否不足以及是否频繁 Rebalance。解决上可以增加消费者实例,但消费者并行度受分区数限制;也可以增加分区数、调大 fetch.max.bytes 和 max.poll.records、批量处理消息、优化数据库写入、扩容下游服务。


26.15 Kafka 为什么不直接删除已消费消息?

Kafka 是发布订阅模型,不同消费者组之间互不影响。一个消息可能被多个消费者组消费,如果某个消费者消费完就删除,会影响其他消费者组。因此 Kafka 不按消费行为删除消息,而是按日志保留策略删除或压缩,例如按时间、大小删除,或者使用 compact 保留相同 key 的最新值。


27. 最后总复习脑图

text 复制代码
Kafka
├── 概述
│   ├── 消息队列
│   ├── 事件流平台
│   ├── 削峰
│   ├── 解耦
│   └── 异步
│
├── 架构
│   ├── Producer
│   ├── Broker
│   ├── Topic
│   ├── Partition
│   ├── Replica
│   ├── Leader/Follower
│   ├── ISR
│   └── Consumer Group
│
├── Producer
│   ├── Interceptor
│   ├── Serializer
│   ├── Partitioner
│   ├── RecordAccumulator
│   ├── Sender
│   ├── batch.size
│   ├── linger.ms
│   ├── acks
│   ├── retries
│   ├── 幂等性
│   ├── 事务
│   └── 有序性
│
├── Broker
│   ├── Controller
│   ├── Leader 选举
│   ├── AR
│   ├── ISR
│   ├── HW
│   ├── LEO
│   ├── Segment
│   ├── .log
│   ├── .index
│   ├── .timeindex
│   ├── delete
│   ├── compact
│   ├── PageCache
│   └── 零拷贝
│
├── Consumer
│   ├── Consumer Group
│   ├── Coordinator
│   ├── Rebalance
│   ├── Range
│   ├── RoundRobin
│   ├── Sticky
│   ├── CooperativeSticky
│   ├── Offset
│   ├── 自动提交
│   ├── 手动提交
│   ├── 重复消费
│   └── 漏消费
│
├── 可靠性
│   ├── acks=0
│   ├── acks=1
│   ├── acks=all
│   ├── min.insync.replicas
│   ├── replication.factor
│   ├── At Most Once
│   ├── At Least Once
│   └── Exactly Once
│
├── 调优
│   ├── 生产者调优
│   ├── Broker 调优
│   ├── 消费者调优
│   ├── 分区数设计
│   ├── 硬件估算
│   └── 消息积压处理
│
└── 集成
    ├── Flume
    ├── Flink
    └── SpringBoot

28. 面试最终背诵版

Kafka 面试最核心要背下面这段:

Kafka 是一个分布式事件流平台,核心设计是 Topic、Partition、副本和消费者组。Topic 负责消息分类,Partition 负责水平扩展和并行读写,每个 Partition 内部是有序追加日志。为了保证高可用,每个 Partition 有 Leader 和 Follower 副本,生产者和消费者默认只和 Leader 交互,Follower 从 Leader 同步数据,Controller 负责 Broker 上下线感知和 Leader 选举。Kafka 高吞吐主要依赖分区并行、顺序写磁盘、PageCache、零拷贝、批量发送和压缩。可靠性方面,生产端通过 acks=all、retries、幂等性和事务保证不丢不重,Broker 端通过副本、ISR、HW/LEO 保证副本一致,消费者端通过手动提交 Offset 和业务幂等处理重复消费。消费者组通过 Coordinator 管理成员和分区分配,Rebalance 会在成员变化、分区变化、心跳超时或处理超时时触发。生产调优主要看 batch.size、linger.ms、buffer.memory、compression.type,消费调优主要看 fetch.max.bytes、max.poll.records、分区数和下游处理能力。


29. 后端开发校招 Kafka 高频追问补充

29.1 Kafka、RabbitMQ、RocketMQ 怎么选?

Kafka

特点:

  • 高吞吐;
  • 强持久化;
  • 适合日志、埋点、流式数据、数据管道;
  • 分区并行能力强;
  • 天然适合大数据和实时计算场景。

不足:

  • 延迟消息、死信队列等传统 MQ 功能不是原生强项;
  • 单条消息低延迟场景不一定最优;
  • 多分区不保证全局有序。

适合:

  • 日志采集;
  • 用户行为埋点;
  • 实时数仓;
  • 大规模消息流;
  • 流式处理;
  • 高吞吐异步解耦。

RabbitMQ

特点:

  • 功能丰富;
  • 路由模型灵活;
  • 支持复杂 Exchange;
  • 单机吞吐不如 Kafka;
  • 更偏传统业务消息。

适合:

  • 中小规模业务系统;
  • 对路由规则要求复杂;
  • 对消息确认机制要求较细;
  • 业务异步解耦。

RocketMQ

特点:

  • Java 生态友好;
  • 支持事务消息;
  • 支持延迟消息;
  • 支持顺序消息;
  • 更适合互联网业务消息场景。

适合:

  • 订单;
  • 支付;
  • 电商交易;
  • 延迟消息;
  • 分布式事务最终一致性。

面试回答模板

如果是日志采集、用户行为埋点、实时数据管道、大吞吐流式场景,我会优先选择 Kafka,因为 Kafka 通过分区并行、顺序写磁盘、PageCache、零拷贝和批量发送实现了非常高的吞吐。如果是传统业务消息,并且需要复杂路由,可以考虑 RabbitMQ。如果是电商交易、订单支付、延迟消息、事务消息等场景,RocketMQ 的业务特性更完整。所以 MQ 选型不是谁绝对更好,而是要看吞吐量、可靠性、延迟、顺序性、事务消息、延迟消息和生态支持。


29.2 为什么不用 MySQL 当消息队列?

MySQL 可以用表模拟消息队列:

sql 复制代码
CREATE TABLE message_queue (
    id BIGINT PRIMARY KEY,
    msg TEXT,
    status INT,
    create_time DATETIME
);

生产者插入数据,消费者轮询查询。

但是问题很多:

  1. 轮询数据库压力大;
  2. 高并发下锁竞争严重;
  3. 消费状态管理复杂;
  4. 不适合高吞吐消息流;
  5. 扩展性差;
  6. 消息堆积会导致表越来越大;
  7. 缺少消费者组、分区、副本、Offset 等 MQ 原生机制。

面试回答:

MySQL 可以模拟消息队列,但不适合作为真正的 MQ。因为消费者需要轮询数据库,会给数据库带来很大压力;高并发下还会有锁竞争和状态更新问题;消息堆积后表会越来越大,查询和维护成本很高。Kafka 这类 MQ 天然提供 Topic、Partition、副本、消费者组、Offset、批量拉取和持久化日志,更适合高吞吐、可扩展的消息场景。


29.3 为什么不用 Redis List 当消息队列?

Redis List 可以实现:

text 复制代码
LPUSH queue msg
BRPOP queue

但是问题是:

  1. Redis 主要是内存系统,成本较高;
  2. 持久化可靠性不如 Kafka 日志;
  3. 消费者组能力不如 Kafka;
  4. 消息堆积会占用大量内存;
  5. 不适合超大规模日志流;
  6. 多副本一致性和消费 Offset 管理不如 Kafka 专业。

面试回答:

Redis 可以用 List 或 Stream 做轻量消息队列,但它更适合低延迟、小规模、轻量级消息场景。Kafka 更适合大吞吐、强持久化、可回放、多消费者组订阅和流式数据处理。Redis 消息堆积会占用大量内存,而 Kafka 基于磁盘日志和 PageCache,更适合存储大规模消息流。


29.4 Kafka 是 Pull 还是 Push?为什么?

Kafka Consumer 使用 Pull 模式。

也就是:

text 复制代码
Consumer 主动从 Broker 拉取消息

不是 Broker 主动推给 Consumer。

为什么使用 Pull?

优点:

  1. Consumer 可以根据自己的处理能力控制拉取速度;
  2. 可以批量拉取,提高吞吐;
  3. Broker 不需要感知每个 Consumer 的处理能力;
  4. 避免 Push 太快导致 Consumer 被打垮;
  5. Consumer 可以自主控制 Offset。

缺点:

  1. 如果没有消息,Consumer 可能空轮询;
  2. 所以 Kafka 提供了长轮询参数,比如 fetch.max.wait.ms

面试回答:

Kafka 使用 Pull 模式,消费者主动从 Broker 拉取消息。这样消费者可以根据自己的处理能力控制消费速度,也可以批量拉取,提高吞吐。如果使用 Push 模式,Broker 很难知道每个消费者的处理能力,推送太快可能压垮消费者。Pull 的缺点是没有数据时可能空轮询,所以 Kafka 通过 fetch.max.wait.ms 等参数支持阻塞等待和批量拉取。


29.5 Kafka 消息丢失怎么排查?

消息丢失要从三端排查:

text 复制代码
Producer → Broker → Consumer

1)Producer 端丢失

可能原因:

properties 复制代码
acks=0
retries=0

或者发送失败后业务没有处理异常。

解决:

properties 复制代码
acks=all
retries>0
enable.idempotence=true

并且发送时关注 callback 异常。


2)Broker 端丢失

可能原因:

text 复制代码
副本数太少
min.insync.replicas=1
Leader 写入后还没同步就宕机
unclean leader election

解决:

properties 复制代码
replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false

3)Consumer 端丢失

常见原因:

text 复制代码
先提交 Offset,后处理业务

如果 Offset 已提交,但业务处理失败,重启后不会再消费这条消息,就造成漏消费。

解决:

text 复制代码
先处理业务
业务成功后再提交 Offset

并关闭自动提交:

properties 复制代码
enable.auto.commit=false

面试回答模板

Kafka 消息丢失要从生产者、Broker 和消费者三端排查。生产者端如果 acks=0 或发送失败没有重试,可能丢消息,所以重要业务要配置 acks=all、retries 和幂等性;Broker 端如果副本数不足、min.insync.replicas 太小,Leader 宕机时也可能丢消息,所以一般配置 replication.factor=3、min.insync.replicas=2;消费者端如果先提交 Offset 再处理业务,业务失败后就会漏消费,所以生产中通常关闭自动提交,在业务处理成功后手动提交 Offset。


29.6 Kafka 重复消费怎么解决?

Kafka 默认更容易实现:

text 复制代码
At Least Once

也就是:

text 复制代码
不丢,但可能重复

重复消费常见场景:

text 复制代码
1. Consumer 拉取消息
2. 业务处理成功
3. Offset 还没提交
4. Consumer 宕机
5. 重启后从旧 Offset 继续消费

解决核心:

text 复制代码
业务幂等

常见幂等方案

1)数据库唯一键

例如订单消息:

sql 复制代码
CREATE UNIQUE INDEX uk_order_msg ON order_table(order_id);

重复插入会失败。


2)消费记录表
sql 复制代码
CREATE TABLE consumed_message (
    msg_id VARCHAR(128) PRIMARY KEY,
    consume_time DATETIME
);

消费前先判断 msg_id 是否存在。


3)Redis 去重
text 复制代码
SETNX msg_id 1

如果设置成功,说明第一次消费;如果失败,说明重复消息。


4)业务状态机

例如订单状态:

text 复制代码
待支付 → 已支付 → 已发货

如果订单已经是"已支付",再次收到支付成功消息时直接忽略。


面试回答模板

Kafka 出现重复消费是正常的,因为 Kafka 通常保证至少一次语义。比如消费者处理完业务后还没提交 Offset 就宕机,重启后会重新消费旧消息。解决重复消费不能只依赖 Kafka,而要在业务侧做幂等。常见方式包括数据库唯一键、消费记录表、Redis SETNX 去重、业务状态机校验等。实际项目中一般是业务处理成功后手动提交 Offset,同时通过业务唯一 ID 保证重复消息不会造成重复写入。


29.7 Kafka 是否支持延迟消息?

Kafka 原生不擅长延迟消息。

不像 RocketMQ 那样直接内置延迟消息等级。

Kafka 可以通过一些方式模拟:

  1. 创建多个延迟 Topic;
  2. 消费者判断消息时间,未到期则暂缓处理;
  3. 使用时间轮;
  4. 借助外部调度系统;
  5. 使用 Kafka Streams 或 Flink 做定时处理。

面试回答:

Kafka 原生不是专门为延迟消息设计的,它更适合高吞吐日志流和事件流。如果业务强依赖延迟消息,比如订单 30 分钟未支付自动取消,RocketMQ 会更直接。Kafka 也可以通过延迟 Topic、时间轮、定时扫描或 Flink/Kafka Streams 实现类似能力,但复杂度会更高。


29.8 Kafka 是否有死信队列?

Kafka 没有像某些 MQ 那样强内置死信队列概念,但可以通过业务方式实现。

常见做法:

text 复制代码
正常 Topic:order-topic
失败 Topic:order-topic-dlt

如果消费失败超过一定次数,就写入死信 Topic。

text 复制代码
order-topic → Consumer → 处理失败 → order-topic-dlt

死信 Topic 中可以保存:

  • 原始消息;
  • 失败原因;
  • 失败时间;
  • 重试次数;
  • 异常堆栈。

面试回答:

Kafka 本身没有强内置的死信队列模型,但可以通过死信 Topic 实现。消费者处理失败后可以重试,超过最大重试次数后,把消息写入专门的 DLT Topic,比如 order-topic-dlt,后续由人工或补偿任务处理。这样可以避免坏消息一直阻塞正常消费。


29.9 Kafka 分区数为什么只能增加,不能减少?

Kafka 支持增加分区,不支持直接减少分区。

原因:

  1. 删除分区会导致该分区数据怎么处理的问题;
  2. 如果直接删除,会造成消息丢失;
  3. 如果迁移到其他分区,会破坏原有分区内顺序;
  4. Offset 映射关系会变复杂;
  5. 消费者组的消费进度也难以保持一致。

面试回答:

Kafka 分区数可以增加,但不能直接减少。因为减少分区会带来数据归属问题:被删除分区中的数据是删除还是迁移?删除会导致数据丢失,迁移到其他分区又可能破坏分区内有序性,同时 Offset 和消费者组消费进度也很难维护。所以 Kafka 选择不支持直接减少分区。如果确实要减少,一般是新建一个分区数更少的 Topic,然后做数据迁移。


29.10 KRaft 和 Zookeeper 是什么关系?

早期 Kafka 使用 Zookeeper 管理元数据。

Zookeeper 负责:

  • Broker 注册;
  • Controller 选举;
  • Topic 元数据;
  • 分区 Leader 信息;
  • ISR 信息。

Kafka 新版本引入 KRaft 模式,逐步去掉 Zookeeper 依赖。

KRaft 使用 Kafka 自己的 Raft Quorum 管理元数据。

面试回答:

Kafka 早期依赖 Zookeeper 管理集群元数据,比如 Broker 注册、Controller 选举、Topic、Partition、Leader 和 ISR 信息。后来 Kafka 引入 KRaft 模式,用 Kafka 自己的 Raft 元数据仲裁机制替代 Zookeeper,从而简化架构、减少外部依赖。校招中一般知道这个演进即可,重点仍然是理解 Controller、Leader 选举和元数据管理。


29.11 Kafka 线上需要监控哪些指标?

常见监控指标:

Broker 侧

  • Broker 是否存活;
  • 磁盘使用率;
  • 网络 IO;
  • 请求延迟;
  • PageCache 命中情况;
  • Under Replicated Partitions;
  • Offline Partitions;
  • ISR 频繁变化情况。

Producer 侧

  • 发送 TPS;
  • 发送延迟;
  • 发送失败率;
  • 重试次数;
  • batch 大小;
  • buffer 使用率。

Consumer 侧

  • Consumer Lag;
  • 消费 TPS;
  • Offset 提交速率;
  • Rebalance 次数;
  • poll 间隔;
  • 消费失败数。

面试回答模板

Kafka 线上监控我会重点看 Broker、Producer 和 Consumer 三侧。Broker 侧关注磁盘、网络、请求延迟、Under Replicated Partitions、Offline Partitions 和 ISR 变化;Producer 侧关注发送速率、发送延迟、失败率、重试次数和 buffer 使用率;Consumer 侧最重要的是 Consumer Lag,还要看消费速率、Offset 提交、Rebalance 次数和消费失败数。尤其是 Lag 持续增长,通常说明生产速度大于消费速度,需要及时扩容或优化消费链路。


29.12 后端校招 Kafka 最终问题清单

面试前至少能回答下面这些问题:

  1. Kafka 是什么?
  2. Kafka 有哪些使用场景?
  3. Kafka 为什么适合日志和埋点?
  4. Kafka 和 RabbitMQ、RocketMQ 怎么选?
  5. 为什么不用 MySQL 或 Redis 做消息队列?
  6. Kafka 的基础架构是什么?
  7. Topic、Partition、Broker、Replica 分别是什么?
  8. Partition 有什么作用?
  9. Kafka 如何保证高吞吐?
  10. Kafka 为什么顺序写磁盘还很快?
  11. PageCache 在 Kafka 中有什么作用?
  12. Kafka 零拷贝是什么?
  13. Producer 发送消息流程是什么?
  14. RecordAccumulator 是什么?
  15. batch.size 和 linger.ms 有什么作用?
  16. Kafka 默认分区策略是什么?
  17. 如何保证同一个订单的消息有序?
  18. Kafka 能保证全局有序吗?
  19. acks=0、1、all 有什么区别?
  20. ISR 是什么?
  21. AR、ISR、OSR 是什么?
  22. HW 和 LEO 是什么?
  23. Kafka 如何保证消息不丢?
  24. Kafka 为什么会重复消费?
  25. 如何解决重复消费?
  26. Kafka 幂等性原理是什么?
  27. Kafka 事务解决什么问题?
  28. Exactly Once 怎么理解?
  29. Consumer Group 是什么?
  30. 一个分区能否被同组多个消费者消费?
  31. 一个消费者能否消费多个分区?
  32. Consumer 是 Pull 还是 Push?
  33. Rebalance 是什么?
  34. Rebalance 什么时候触发?
  35. 如何减少 Rebalance 的影响?
  36. Offset 是什么?
  37. Offset 存在哪里?
  38. 自动提交和手动提交 Offset 有什么区别?
  39. 什么情况下会漏消费?
  40. 什么情况下会重复消费?
  41. Kafka 消息积压怎么排查?
  42. Kafka 分区数怎么设计?
  43. 分区数能不能减少?为什么?
  44. Kafka 日志文件怎么存储?
  45. Segment、index、timeindex 是什么?
  46. Kafka 日志删除和 compact 有什么区别?
  47. Kafka 是否支持延迟消息?
  48. Kafka 如何实现死信队列?
  49. KRaft 和 Zookeeper 有什么关系?
  50. Kafka 线上应该监控哪些指标?
相关推荐
ZC跨境爬虫2 小时前
跟着 MDN 学 HTML day_10:(超链接核心语法+路径规则)
前端·css·笔记·ui·html·edge浏览器
GISer_Jing2 小时前
AI原生前端工程化进阶实践:从流式交互架构到端云协同全链路落地
前端·人工智能·后端·学习
东京老树根2 小时前
SAP学习笔记 - BTP CAP开发02 - Assosication,Composition,?$expand=books
笔记·学习
Slow菜鸟10 小时前
AI学习篇(五) | awesome-design-md 使用说明
人工智能·学习
ZC跨境爬虫10 小时前
跟着 MDN 学 HTML day_9:(信件语义标记)
前端·css·笔记·ui·html
狐狐生风11 小时前
LangChain 向量存储:Chroma、FAISS
人工智能·python·学习·langchain·faiss·agentai
狐狐生风11 小时前
LangChain RAG 基础
人工智能·python·学习·langchain·rag·agentai
小江的记录本12 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
努力努力再努力FFF13 小时前
医生对AI辅助诊断感兴趣,作为临床人员该怎么了解和学习?
人工智能·学习