kafka
一、kafka概述
kafka如何实现高性能
- 增加消费者,提升消息的处理速度。相应的可以增加生产者的数量。
- 将消息队列分类,每一类为一个topic,生产者将消息按照topic将不同的消息投递到消息队列中,消费者消费对应topic的消息
- 分区并行消费:如果一个topic中的消息过多,可以将消息拆分为几段,每段为一个partition分区,一个消费者负责一个partition
kafka如何实现高扩展性
- Topic 多分区:单台机器的资源有限,可以将partition分散在不同机器上,每台机器代表一个broker
- Broker 集群扩容:可以通过增加broker来缓解CPU和内存占用过高的问题
- 消费能力不足时,增加消费者组内的消费者数量(最多不超过分区数),即可线性提升消费速度。
kafka如何实现高可用
- 可以给partition增加几个副本,分为leader副本和follower副本,leader负责处理生产者和消费者之间的读写请求,follower负责异步同步leader的内容
- 将leader和follower分散到不同的borker上,可以避免leader挂掉后消息不可用
- ISR(同步副本集合)机制:只有与 Leader 保持同步的副本才能被选举为新 Leader,保证数据不丢失、不不一致。
- ACK 可靠性策略:生产者可配置 acks=all,确保所有 ISR 副本都落盘后才认为发送成功,保证消息不丢失。
二、consummer group的作用
1、实现消息的并行消费
- 单个消费者的处理能力有限,无法高效处理高吞吐量的消息流。
- 消费者组允许多个消费者协同消费同一个 Topic 的分区,每个分区仅由组内的一个消费者处理,从而实现水平扩展
2、负载均衡
- 分区数量固定时,消费者组自动平衡分区分配(通过内置策略如 Range、RoundRobin、Sticky)。
3、容错与高可用
- 消费者组通过心跳机制检测故障,触发 Rebalance 将分区重新分配给存活的消费者。避免单个消费者崩溃时,造成消息积压或丢失
4、消息消费进度的统一管理
- 消费者组维护统一的 Offset:
- 每个消费者组的消费进度(Offset)存储在 Kafka 内部 Topic __consumer_offsets 中。
- 即使消费者重启或替换,也能从上次提交的 Offset 恢复,避免重复消费或丢失消息
三、zookeeper的作用
- 组件太多了,需要统一维护组件的状态信息。zookeeper会定期和borker通信获取kafka集群的状态
四、kafka中的重要概念
主题(topic)
- Kafka中的消息以主题为单位进行归类,生产者负责将消息发送到特定的主题(发送到Kafka集群中的每一条消息都要指定一个主题),而消费者负责订阅主题并进行消费。
- 主题是一个逻辑上的概念,可以包含多个分区。
- 同一主题下的不同分区包含的消息是不同的。
分区(partition)
- 分区在存储层面可以看作成一个可追加的日志(Log)文件,消息在被追加到分区日志文件的时候都会分配一个特定的偏移量(offset)
- offset是消息在分区中的唯一标识,Kafka通过它来保证消息在分区内的顺序性,不过offset并不跨越分区
副本的集合AR(Assigned Replicas)、ISR(In-Sync Replicas)、OSR(Out-of-Sync Replicas)
- 分区中的所有副本统称为AR。
- 所有与leader副本保持一定程度同步的副本(包括leader副本)组成ISR,ISR集合是AR集合中的一个子集。
- 与leader副本同步滞后过多的副本(不包括leader副本)组成OSR。由此可见,AR=ISR+OSR。
- 消息会先发送到leader副本,然后follower副本才能从leader副本中拉取消息进行同步,同步期间内follower副本相对于leader副本而言会有一定程度的滞后。该时间阈值由replica.lag.time.max.ms参数设定,默认30s
LEO和HW
- LEO是Log End Offset的缩写,它标识当前日志文件中下一条待写入消息的offset,即LEO的大小相当于当前日志分区中最后一条消息的offset值加1。
- HW是High Watermark的缩写,俗称高水位,它标识了一个特定的消息偏移量(offset),消费者只能拉取到这个offset之前的消息。
- ISR集合中最小的LEO即为分区的HW,对消费者而言只能消费HW之前的消息。
- 图解LEO和HW
![image]()
分区策略
- kafka默认分区器DefaultPartitioner
- 规则1:指明partition的情况下,直接将指明的值作为partition值
- 例如partition=0,所有数据写入分区0
- 规则2:没有指明partition值但有key的情况下,将key的hash值与topic的partition数进行取余得到partition值;
- 例如:key1的hash值=5,key2的hash值=6,topic的partition数=2,那么key1对应的value1写入1号分区,key2对应的value2写入0号分区。
- 规则3:既没有partition值又没有key值的情况下,Kafka采用Sticky Partition(黏性分区器),会随机选择一个分区,并尽可能一直使用该分区,待该分区的batch已满或者已完成,Kafka再随机一个分区进行使用(和上一次的分区不同)
- 自定义分区,研发人员可以根据企业需求,自己重新实现分区器
kafka消费发送流程
- 1、生产者的main线程调用send方法创建ProducerRecord对象来封装消息,包含主题、key、value。
- 2、拦截器对消息预处理(如添加审计信息),序列化器将key/value转换为字节数组,分区器确定消息的目标分区
- 若指定key,则按key哈希分配(相同key到同一分区)
- 若无key,则轮询或粘性分区(Sticky Partitioning
- 3、消息按照topic-partition分组存入内存缓冲区,等待批量发送,
- 缓冲区默认32m,由参数buffer.memory控制
- 每个分区对应一个Deque< ProducerBatch >。Deque(双端队列),支持从队列头部或尾部快速插入/删除数据。ProducerBatch是一个包含多条消息的批次,是Kafka网络传输的最小单位。
- 参数batch.size,控制单个ProducerBatch的最大容量(默认16KB)
- linger.ms,决定批次在Deque中的等待时间
- 4、Sender线程(独立I/O线程)轮询缓冲区中的消息异步发送到broker
- 满足以下任一条件即发送:批次满(batch.size)、等待超时(linger.ms)、缓冲区满(buffer.memory)
- 按topic-partition选择目标Broker(通过元数据缓存定位Leader副本)
- 5、Leader将消息写入页缓存(PageCache)(非直接落盘,依赖OS刷盘机制),leader将消息同步给副本,leader根据ACK应答级别给sender线程发送确认。
- acks=1(默认):仅Leader写入成功即响应
- acks=all:所有ISR副本同步完成才响应
- acks=0:不等待响应(可能丢失消息)
- 6、sender收到确认后将缓冲区中的数据清除。main线程通过回调函数(callback)异步获取消息发送结果,避免阻塞
![image]()
生产者提高吞吐量的设置参数
- batch.size:批次大小,默认16k
- linger.ms:等待时间,修改为5-100ms
- compression.type:传输启用压缩,方式为snappy
- buffer.memory:缓冲区大小,默认32m,修改为64m
kafka的数据可靠性
kafka的ACK应答级别
- acks=0:producer发出消息即代表完成发送,不需要等待来自borker的落盘应答。这种情况下数据传输效率最高,但是数据可靠性确是最低的
- acks=1:producer发出消息,等待 Leader 副本写入成功,但不等待 Follower同步。
- acks=-1(a11):生产者发送过来的数据,Leader和isr队列中的节点都落盘后才表示写入成功
数据完全可靠条件
- ACK级别设置为-1
- 分区副本大于等于2
- ISR里应答的最小副本数量大于等于2
kafka数据传递语义
- Kafka 的数据传递语义(Message Delivery Semantics)定义了消息在生产者、Broker 和消费者之间的可靠性保证,主要分为以下三种级别
最多一次(AtMost Once)
- 语义:消息可能丢失,但绝不会重复消费
- 实现条件
- 生产者的ACK级别设置为0
至少一次(AtLeast Once)
- 语义:消息绝不会丢失,但可能重复消费
- 实现条件
- ACK级别设置为-1
- 分区副本数大于等于2
- ISR里应答的最小副本数量大于等于2
精确一次(ExactlyOnce)
- 语义:消息既不丢失也不重复
- 实现条件
- 幂等性
- ACK级别设置为-1
- 分区副本数大于等于2
- ISR里应答的最小副本数量大于等于2
消息重复场景
- 生产者重试:
- 当生产者发送消息后,如果leader副本写入成功但未收到所有ISR(in-sync replicas)副本的确认。(ack=1 或 ack=-1)
- 生产者会重试发送,可能导致消息被重复写入
- Leader切换:
- 如果leader在发送ack后但在将消息传播给所有follower之前崩溃,新leader可能不包含该消息,(ack=1 或 ack=-1),导致生产者重试
- 消费者提交偏移量失败:
- 消费者处理完消息后,提交offset前崩溃,下次启动时会从最后提交的offset重新消费
漏消费场景
- 自动提交的间隔设置较短,消息尚未处理完成,就被自动提交
# 危险配置
enable.auto.commit=true
auto.commit.interval.ms=1000 # 自动提交间隔过短
- 消息处理逻辑异常,比如逻辑设置先提交后处理,但是消息处理过程失败了
- 日志清理策略导致,log.retention.ms设置过短,消息被删除后消费者才来消费
kafka-configs --describe --topic my-topic --all | grep retention
- 消费者seek跳过消息,手动定位offset时跳过了某些消息
kafka幂等性
- Kafka 的幂等性是一种保证消息不重复的机制,它能确保生产者发送的消息即使因网络问题或重试导致多次发送,Broker 也只会持久化一次。
如何启用幂等性
- 配置生产者参数 enable.idempotence 为 true
- 启用后,Kafka 会自动设置以下参数:
- acks=all(必须确保消息被完整接收)
- retries=Integer.MAX_VALUE(无限重试)
- max.in.flight.requests.per.connection=1(同一时间只能有一个未确认的请求)
幂等性原理
-
Kafka 通过以下关键标识符实现幂等性:
- PID(Producer ID):每个生产者实例的唯一标识。Kafka每次重启都会分配一个新的
- Sequence Number:每条消息的序列号(针对每个分区单调递增)
-
Broker 会检查这组标识符:
- 如果收到相同 PID + 分区 + Sequence Number 的消息,会识别为重复消息并丢弃
- 如果 Sequence Number <= 已存储的序列号,则判定为重复消息,直接丢弃。
- 如果 Sequence Number == 已存储的序列号 + 1,则接受消息并更新存储的序列号。
- 如果 Sequence Number > 已存储的序列号 + 1(乱序),则拒绝消息(可能发生数据丢失)。
幂等性的限制
- 仅保证单分区单会话内的幂等:
- 不能跨分区保证幂等
- 不能跨生产者会话保证幂等(重启后PID会变)
- 不保证跨分区的原子性:
- 如果需要跨分区/跨Topic的原子写入,需要使用事务(Transactions)
kafka事务
- Kafka事务(Transactions)提供了跨分区和跨Producer会话的原子性写入能力,能够确保消息"要么全部成功,要么全部失败",是构建可靠流处理系统的关键特性。
![image]()
事务关键配置
- 生产者配置
- 必须配置 transactional.id(唯一标识一个事务型 Producer)
- 必须启用幂等性(enable.idempotence=true)
- ack级别设置为-1(all)
- 消费者配置
- 只读取已提交消息(isolation.level=read_committed)
事务实现原理
- Kafka Broker 中有一个 事务协调器(Transaction Coordinator),负责管理事务的提交/回滚
- 每个 transactional.id 会被哈希到某个 Broker,由其 Transaction Coordinator 管理
- Kafka 内部有一个特殊的 Topic __transaction_state,用于持久化事务元数据(如事务状态、PID 映射等)
使用场景
- 精确一次处理(Exactly-once)
- 流处理应用:如Kafka Streams的状态更新
- 跨系统事务:与数据库事务配合(通过Kafka Connect)
- 关键业务操作:如金融交易、订单处理
最佳实践
- 为每个逻辑Producer分配唯一transactional.id
- 事务应尽量短小,避免长时间运行
- 合理设置transaction.timeout.ms(默认60秒)
- 监控事务相关指标:提交/中止比率、平均持续时间等
- 与幂等性配合使用(enable.idempotence=true)
kafka数据有序性
- kafka保证在单个分区内,消息严格按照发送顺序存储和消费
- 实现机制:
- 生产者按顺序发送消息到分区
- 分区在Broker上是追加写入的日志(Log)
- 消费者按偏移量(Offset)顺序读取
- 不同分区之间的消息没有顺序保证
影响有序性的关键因素
- 生产者消息发送的重试机制:
- 默认配置下重试可能破坏顺序
- 解决方案:设置 max.in.flight.requests.per.connection=1-
- 生产者消息的批量发送:
- 批量发送可能影响消息实际到达顺序
- 解决方案:调整 linger.ms 和 batch.size
- Leader切换:
- 在 unclean leader election 情况下可能丢失顺序
- 解决方案:设置 unclean.leader.election.enable=false
- 消费者的并行消费:
- 单个消费者多线程消费会破坏顺序
- 解决方案:每个分区使用单线程消费
保证有序性的配置方案
# 生产者配置
# 保证分区内有序的最小配置
acks=all
max.in.flight.requests.per.connection=1
retries=Integer.MAX_VALUE
enable.idempotence=true
# 消费者配置
# 保证顺序消费
max.poll.records=1 # 每次只拉取一条(极端情况)
fetch.min.bytes=1 # 避免等待批量拉取
kafka命令行工具
- Kafka 的命令行工具在 Kafka 包的/bin目录下,主要包括服务和集群管理脚本,配置脚本,信息查看脚本,Topic 脚本,客户端脚本等。
- kafka-configs.sh:配置管理脚本
- kafka-console-consumer.sh:kafka 消费者控制台
- kafka-console-producer.sh:kafka 生产者控制台
- kafka-consumer-groups.sh:kafka 消费者组相关信息
- kafka-delete-records.sh:删除低水位的日志文件
- kafka-log-dirs.sh:kafka 消息日志目录信息
- kafka-mirror-maker.sh:不同数据中心 kafka 集群复制工具
- kafka-preferred-replica-election.sh:触发 preferred replica 选举
- kafka-producer-perf-test.sh:kafka 生产者性能测试脚本
- kafka-reassign-partitions.sh:分区重分配脚本
- kafka-replica-verification.sh:复制进度验证脚本
- kafka-server-start.sh:启动 kafka 服务
- kafka-server-stop.sh:停止 kafka 服务
- kafka-topics.sh:topic 管理脚本
- kafka-verifiable-consumer.sh:可检验的 kafka 消费者
- kafka-verifiable-producer.sh:可检验的 kafka 生产者
- zookeeper-server-start.sh:启动 zk 服务
- zookeeper-server-stop.sh:停止 zk 服务
- zookeeper-shell.sh:zk 客户端
创建topic
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
--create \
--topic my_topic_name \
--partitions 20 \
--replication-factor 3 \
--config x=y
修改主题
- Kafka 目前不支持减少主题的分区数
# 修改主题的分区数量为40个
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
--alter \
--topic my_topic_name \
--partitions 40
添加配置
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name my_topic_name \
--alter \
--add-config x=y
删除配置
bin/kafka-configs.sh --bootstrap-server localhost:9092 \
--entity-type topics \
--entity-name my_topic_name \
--alter \
--delete-config x
删除主题
bin/kafka-topics.sh --bootstrap-server localhost:9092 \
--delete \
--topic my_topic_name
启动生产者
./kafka-console-producer.sh --bootstrap-server KAFKA_IP:9092 --topic TOPIC_NAME
启动消费者
./kafka-console-consumer.sh --bootstrap-server KAFKA_IP:9092 --consumer-property group.id=GROUP_NAME --topic TOPIC_NAME
生产者将消息发送给broker,broker会将消息保存在本地的日志文件中
/usr/local/kafka/data/kafka-logs/主题-分区/00000000.log
消息的保存是有序的,通过offset偏移量来描述消息的有序性
单播消息
如果多个消费者在同一个消费组,只有一个消费者可以收到订阅的topic中的消息
多播消息
不同的消费组订阅同一个topic,那么不同的消费组中只有一个消费者能收到消息
查看消费者详细信息
./kafka-consumer-group.sh --bootstrap-server KAFKA_IP:9092 --describe --group GROUP_NAME
查看所有消费组列表
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
查看指定消费组的详细信息
# 输出中的LAG字段表示滞后消息数(LOG-END-OFFSET 减去 CURRENT-OFFSET)
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group my-group --describe
# 如果只关心 Lag(积压消息数),可以结合 awk 筛选
# 只看GROUP 、TOPIC 、PARTITION 、LAG这四个字段
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group my-group --describe | awk '{print $1,$2,$3,$6}'
查看消费组状态
# Stable:消费组正常运行,所有分区已分配。
# Empty:消费组存在,但没有活跃的消费者。
# Dead:消费组已无消费者,且所有 offset 已过期(通常 __consumer_offsets 已清理)
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group my-group --state
查看消费者成员信息
# #PARTITIONS:该消费者负责的分区数。
# ASSIGNMENT:具体分配的分区列表
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group my-group --members --verbose
重置消费组 Offset
- 如果需要手动调整消费位置(如重新消费或跳过积压数据)
- 可选重置策略:
- --to-earliest:将偏移量重置为最早偏移量。
- --to-latest:将偏移量重置为最新偏移量(丢弃积压数据)。
- --to-current :将偏移量重置为当前偏移量
- --to-datetime 2023-01-01T00:00:00.000:按时间重置。
- --shift-by -100:向后移动 100 条消息(负数表示回退)。
./kafka-consumer-groups.sh --bootstrap-server localhost:9092 \
--group my-group \
--reset-offsets \
--to-earliest \
--topic my-topic \
--execute
zookeeper

- [zk: localhost:2181(CONNECTED) 2] ls /kafka
- 这个命令是用于查看 ZooKeeper中 /kafka 路径下的子节点,通常用于Kafka 集群管理。
- Kafka 依赖 ZooKeeper 存储集群元数据,/kafka 是 Kafka 的根路径
Controller(控制器)
- Controller 是从 Broker 中选举出来的,负责分区 Leader 和 Follower 的管理。
- 当某个分区的 leader 副本发生故障时,由 Controller 负责为该分区选举新的 leader 副本,当检测到某个分区的 ISR(In-Sync Replica)集合发生变化时,由Controller负责通知所有 broker 更新其元数据信息。
- 当使用kafka-topics.sh脚本为某个 topic 增加分区数量时,同样还是由控制器负责分区的重新分配。
- Kafka 中 Contorller 的选举的工作依赖于 Zookeeper,成功竞选为控制器的 broker 会在 Zookeeper 中创建/controller这个临时(EPHEMERAL)节点。


broker节点服役和退役
服役新节点的过程
启动新节点
bin/kafka-server-start.sh -daemon ./config/server.properties
指定需要重新分配分区的主题列表
# 情况1,迁移指定主题
cat topics-to-move.json
{
"topics": [
{"topic": "topic1"},
{"topic": "topic2"}
],
"version": 1
}
# 情况2,迁移主题中的特定分区
{
"partitions": [
{"topic": "topic1", "partition": 0},
{"topic": "topic1", "partition": 1}
],
"version": 1
}
# 情况3,迁移所有主题
# 先获取所有主题
kafka-topics.sh --list --bootstrap-server localhost:9092 > all_topics.txt
# 转换为JSON格式
awk '{print "{\"topic\": \""$1"\"},"}' all_topics.txt > temp.json
echo '{ "topics": [' > topics.json
cat temp.json >> topics.json
echo '], "version": 1 }' >> topics.json
# JSON 格式必须严格正确(可使用 jq 验证)
jq . topics.json
生成分区重新分配计划
# 生成分区重新分配计划(包含新节点)
kafka-reassign-partitions.sh --bootstrap-server kafka101:9092\
--broker-list "0,1,2,3" \
--topics-to-move-json-file topics.json \
--generate \
> reassign.json
执行重新分配
kafka-reassign-partitions.sh --bootstrap-server kafka101:9092 \
--reassignment-json-file reassign.json \
--execute \
验证分配状态
kafka-reassign-partitions.sh --bootstrap-server kafka101:9092 \
--reassignment-json-file reassign.json \
--verify \
退役旧节点
1、指定需要重新分配分区的主题列表
2、生成分区重新分配计划(不包含退役节点)
3、执行重新分配
4、验证进度
5、停止kafka服务
kafka的Leader选举流程
Kafka 集群中有一个 broker 的 Controller 会被选举为 Controller Leader,负责管理集群broker 的上下线,所有 topic 的分区副本分配和 Leader 选举等工作。
Controller 的信息同步工作是依赖于 Zookeeper 的

leader、follower故障处理细节
Follower故障
(1)Followrer发生故障后会被临时踢出ISR
(2)这个期间Leader和Follower继续接收数据(3)待该Fo1lower恢复后,Fo1lower会读取本地磁盘记录的上次的HW,并将1og文件高于HW的部分截取掉,从HW开始向Leader进行同步。
(4)等该Follower的LEO大于等于该Partition的HW,即Fo1lower追上Leader之后,就可以重新加入ISR了。
Leader故障
(1)Leader发生故障之后,会从ISR中选出一个新的Leader(2)为保证多个副本之间的数据一致性,其余的Follower会先将各自的1og文件高于HW的部分截掉,然后从新的Leader同步数据。
注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。
手动调整分区副本数量
- 在生产环境中, 每台服务器的配置和性能不一致, 但是Kafka只会根据自己的代码规则创建对应的分区副本, 就会导致个别服务器存储压力较大。 所以需要手动调整分区副本的存储
点击查看代码
# (1)创建一个新的 topic, 名称为 three。
bin/kafka-topics.sh --bootstrap-server hadoop102:9092 \
--create --partitions 4 \
--replication-factor 2 --topic three
#(2)查看分区副本存储情况。
bin/kafka-topics.sh --bootstrap-server hadoop102:9092 \
--describe --topic three
#(3)创建副本存储计划(所有副本都指定存储在broker0、1 中)。
vim increase-replication-factor.json
{
"version":1,
"partitions":[{"topic":"three","partition":0,"replicas":[0,1]},
{"topic":"three","partition":1,"replicas":[0,1]},
{"topic":"three","partition":2,"replicas":[1,0]},
{"topic":"three","partition":3,"replicas":[1,0]}]
}
#(4)执行副本存储计划
bin/kafka-reassign-partitions.sh --bootstrap-server hadoop102:9092 --reassignment-json-file
increase-replication-factor.json --execute
#(5)验证副本存储计划。
[atguigu@hadoop102 kafka]$ bin/kafka-reassign-partitions.sh --
bootstrap-server hadoop102:9092 --reassignment-json-file
increase-replication-factor.json --verify
#(6) 查看分区副本存储情况。
bin/kafka-topics.sh --bootstrap-server
hadoop102:9092 --describe --topic three
leader partition负载平衡
通常auto.leader.rebalance.enable在生产中设置为false,即不开启

增加副本因子
在生产环境当中,由于某个主题的重要等级需要提升,我们考虑增加副本。副本数的增加需要先制定计划,然后根据计划执行
点击查看代码
#1)创建 topic(目前副本数为1)
bin/kafka-topics.sh --bootstrap-server hadoop102:9092 \
--create --partitions 3 --replication-factor 1 --topic four
#2)手动增加副本存储
# 创建副本存储计划(所有副本都指定存储在 broker0、 broker1、 broker2 中)。
vim increase-replication-factor.json
输入如下内容:
{"version":1,"partitions[
{"topic":"four","partition":0,"replicas":[0,1,2]},{"topic":"four","partition":1,"replicas":[0,1,2]},{"topic":"four","partition":2,"replicas":[0,1,2]}]}
#执行副本存储计划。
bin/kafka-reassign-partitions.sh --
bootstrap-server hadoop102:9092 --reassignment-json-file
increase-replication-factor.json --execute
kafka文件存储
partition存储目录结构
- Topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于一个物理文件夹,文件夹中存储的就是实际的消息数据。消息是以日志分段(segment)的形式存储的。文件夹的命名规则为: topic名称+分区序号, 例如: first-0。
- 一个 Topic 可划分为多个 Partition(如 名为orders的topic 分为 orders-0、orders-1)
- Partition 是 Kafka 并行化的最小单元,不同 Partition 可分布在不同 Broker 上。
/kafka-logs/
├── orders-0/ # Topic: orders, Partition: 0
│ ├── 00000000000000000000.index # 偏移量索引文件
│ ├── 00000000000000000000.log # 消息数据文件(核心)
│ ├── 00000000000000000000.timeindex # 时间戳索引文件
│ └── 00000000000000012345.log # 新Segment(达到1GB或7天时创建)
└── orders-1/ # Partition: 1
# index和log文件均以当前segment的第一条消息的offset命名
核心文件类型
-
日志文件(.log)
- 存储实际消息,文件名以当前分段的起始偏移量命名(如 00000000000000000000.log)。
- 写入方式:仅追加(Append-Only),保证顺序写性能。
- 分段策略:
- 大小阈值:log.segment.bytes=1GB(默认)
- 时间阈值:log.roll.hours=168(7天,默认)
-
索引文件(.index)
- 稀疏索引:每写入 log.index.interval.bytes=4KB 数据,记录一条 <Offset, 物理位置> 映射
- 作用:快速定位消息,避免全量扫描。
-
时间索引文件(.timeindex)
- 按时间戳建立索引,支持按时间范围查询(如 offsetsForTimes API)。
kafka消息定位流程
- 1、确定目标 Partition 和 Offset
- 消费者指定要读取的 Topic、Partition 和 Offset(如 orders-0 分区的 Offset=220)
- 2、查找对应的 Segment 文件
- Segment 文件命名是以该segment中第一条索引的名字命名的
- 选择文件名 ≤ 目标 Offset 的最大 Segment。
- 3、二分查找索引文件(.index)
- 索引条目格式:<Offset, 物理位置>(如 200:1024 表示 Offset=200 的消息在 .log 文件的 1024 字节处)。
- 读取 .index 文件,找到 ≤ 目标 Offset 的最大索引条目(如 Offset=200)。
- 根据索引中的物理位置(如 1024),从 .log 文件的对应位置开始顺序扫描,直到找到目标 Offset=220 的消息
- 返回消息内容
解析 .log 文件中的消息头(Header)和负载(Payload),返回给消费者。
文件清理策略
删除策略(delete)
- log.cleanup.policy = delete 启用删除策略
- 基于时间。Kafka 中默认的日志保存时间为 7 天,可以通过调整如下参数修改保存时间。超时的日志会被删除
- log.retention.hours, 最低优先级小时,默认 7 天。
- log.retention.minutes, 分钟。
- log.retention.ms, 最高优先级毫秒。
- log.retention.check.interval.ms,负责设置检查周期,默认5分钟。
- 基于大小。超过设置的所有日志总大小,删除最早的 segment。
- log.retention.bytes = -1,(默认不限制)
压缩策略(compact)
- log.cleanup.policy = compact 启用压缩策略
针对Key:保留同一Key的最新值,适用于用户配置更新的情况,例如用户的年龄变更
运维调试,查看日志
# 监控清理状态
# 查看清理线程状态
kafka-topics.sh --describe --topic user-profile \
--bootstrap-server localhost:9092 | grep "LogCleaner"
# JMX 指标(如 LogCleaner 吞吐量)
kafka-run-class kafka.tools.JmxTool \
--object-name kafka.log:type=LogCleaner
运维命令示例
(1)查看 Topic 的磁盘占用
kafka-log-dirs.sh --bootstrap-server localhost:9092 \
--describe --topic-list orders
(2)手动触发 Segment 滚动
kafka-topics.sh --bootstrap-server localhost:9092 \
--alter --topic orders --config segment.bytes=1073741824
(3)解析 Segment 文件内容
kafka-dump-log.sh --files /data/kafka-logs/orders-0/00000000000000000000.log
(4) 检查特定 Topic 的分区分布
kafka-log-dirs.sh --bootstrap-server localhost:9092 \
--describe --topic-list test
(5) 手动清理过期日志
kafka-log-dirs.sh --bootstrap-server localhost:9092 \
--alter --topic test --partition 0 \
--log-dir /data/kafka-logs \
--config retention.ms=3600000 # 保留1小时
kafka高效读写数据的原因
-
1、 kafka采用分布式分区架构(并行度高)
- Topic 分为多个 Partition:每个 Partition 是一个有序的、不可变的消息队列。
- Producer可同时向不同 Partition 并行写入。
- Consumer Group中不同 Consumer 可并行消费不同 Partition。
-
2、 稀疏索引(快速定位数据)
索引文件(.index):存储消息 Offset 到物理位置的映射(每4KB数据建一条索引)。
日志文件(.log):存储实际消息,文件名以当前 Segment(日志分段) 的起始 Offset 命名。
根据目标 Offset 二分查找 最近的索引条目。
从索引指向的物理位置开始顺序扫描日志文件。 -
3、 顺序写磁盘(写入高性能)
Kafka 的 producer 生产数据,要写入到 log 文件中,写的过程是一直追加到文件末端,为顺序写。省去了大量磁头寻址的时间 -
4、 页缓存 + 零拷贝,实现读取高性能
- 零拷贝
- 传统的文件传输数据需要经过,磁盘 → 内核缓冲区 → 用户缓冲区 → Socket缓冲区 → 网卡。历经(4次拷贝 + 2次CPU上下文切换)
- kafka broker不处理数据,数据的处理是在生产者和消费者端处理。零拷贝kafka传输数据经过,磁盘 → 内核缓冲区 → 网卡(2次拷贝 + 0次CPU切换)
- 页缓存
- PageCache页缓存:Kafa直接利用操作系统提供的PageCache功能。当上层有写操作时,操作系统只是将数据写入PageCache。当读操作发生时,先从PageCache中查找,如果找不到,再去磁盘中读取。实际上PageCache是把尽可能多的空闲内存都当做了磁盘缓存来使用。

kafka消费者
kafka消费方式
- consumer采用从broker中主动拉取数据
- Kafka没有采用push这种方式,因为由broker决定消息发送速率, 很难适应所有消费者的消费速率。
- pull模式不足之处是,如果Kafka没有数据,消费者可能会陷入循环中, 一直返回空数据。
消费者组
- 消费者组由多个消费者组成,同一个消费者组中的消费者的groupid相同
- 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费。
- 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
- 如果向消费组中添加更多的消费者,超过主题分区数量,则有一部分消费者就会闲置,不会接收任何消息。
Coordinator(协调器)
- Coordinator 是一个 Broker,而不是消费者。每个 Kafka Broker 都可以担任某些消费者组的 Coordinator。
- 在 Kafka 中,Coordinator(协调器) 负责管理消费者组(Consumer Group)的元数据。也就是系统队列__consumer_offsets中Topic的对应分区数据
- 负责处理消费者的心跳、触发再平衡(Rebalance)等关键操作。
如何确定协调器
- groupid的hashcode值 % 50(__consumer_offsets的分区个数)
- 如果得到的值为1,那么__consumer_offsets主题的1号分区所在的broker作为该groupid的coordinator
Kafka 消费者组的初始化流程
- 1、消费者启动并订阅 Topic
- 2、确定 Group Coordinator
- 消费者通过 groupid的hashcode值 % 50(__consumer_offsets的分区个数)得到一个分区
- 向集群任意 Broker 发送 FindCoordinator 请求,获取该分区 Leader 所在的 Broker(即 Coordinator)
- 3、消费者向 Coordinator 发送 JoinGroup 请求
- 首次加入组的话,Coordinator 选举第一个注册的消费者作为 Leader 消费者。Leader 消费者负责计算分区分配方案(如 Range、RoundRobin)。
- 非首次加入的情况,直接同步现有分配方案。
- 4、分区分配(SyncGroup)
- Leader 消费者计算分配策略后,通过 SyncGroup 请求提交给 Coordinator。Coordinator 将分配结果同步给所有消费者。
- 5、Offset 初始化
- 消费者根据auto.offset.reset的配置决定从何处开始消费
- 6、开始消费并维持心跳
- 消费者启动独立线程,按 heartbeat.interval.ms(默认3秒)向 Coordinator 发送心跳。
- 若 Coordinator 在 session.timeout.ms(默认45秒)内未收到心跳,触发再平衡。

消费者的重要参数
# 向 Kafka集群建立初始连接用到的 host/port 列表。
bootstrap.servers
# 指定接收消息的 key 和 value 的反序列化类型。一定要写全类名。
key.deserializer 和 value.deserializer
# 指定接收消息的 key 和 value 的反序列化类型。一定要写全类名。
group.id
# 默认值为 true,表示消费者会自动周期性地向服务器提交偏移量。
enable.auto.commit
# 则该值定义了消费者偏移量向 Kafka自动提交的频率
auto.commit.interval.ms
# 当 Katka 首次启动或当前偏移量在服务器中不存在时偏移量的设置策略
# earliest:自动重置偏移量到最早的偏移量。
# latest:默认,自动重置偏移量为最新的偏移量。
# none:如果消费组原来的(previous)偏移量不存在,则向消费者抛异常。
# anything:向消费者抛异常。
auto.offset.reset
# 控制Kafka内部主题__consumer_offsets的分区数量
offsets.topic.num.partitions
# Kafka 消费者和组协调器(Group Coordinator)之间的心跳时间,默认 3s。该条目的值必须小于 session.timeout.ms ,也不应该高于session.timeout.ms 的1/3。超过session.timeout.ms时间无应答被踢出消费者组
heartbeat.interval.ms
# 消费者处理消息的最大时长,默认是5分钟。超过该值,消费者会从消费者组中被移除,消费者组执行再平衡。
max.poll.interval.ms
# 默认1个字节。消费者获取服务器端一批消息最小的字节数。
fetch.min.bytes
# 默认 500ms。如果没有从服务器端获取到一批数据的最小字节数。该时间到,仍然会返回数据。
fetch.max.wait.ms
# 一次 poll 拉取数据返回消息的最大条数,默认是 500 条。
max.poll.records

消费者组的分区分配策略
- Kafka 消费者组的分区分配策略(Partition Assignment Strategy)决定了 Topic 分区如何分配给组内的消费者,直接影响消费的 负载均衡 和 故障恢复。Kafka 提供了多种内置策略,并支持自定义分配逻辑
Range 分配策略
- Range是对每个topic而言的,首先对同一个 topic 里面的分区按照字典顺序(简单理解为序号顺序)进行排序,并对消费者按照字典(字母)顺序进行排序。
- 假如有7个分区(p0,p1,p2,p3,p4,p5,p6),3个消费者(C0,C1,C2)
- C0分配p0、p1、p2
- C1分配p3、p4
- C2分配p5、p6
- 如果有N多个topic,消费者C0可能都会多消费1个分区,容易产生数据倾斜!
RoundRobin 分配策略
- RoundRobin 针对集群中所有Topic而言。
- 将所有分区和消费者按哈希排序,然后轮询分配。
- 例如:3 个分区 (p0, p1, p2),2 个消费者 (c1, c2):
- c1 分配 p0, p2
- c2 分配 p1
- 优点:分配更均衡(尤其适用于消费者订阅相同 Topic 列表)。
- 缺点:如果消费者订阅的 Topic 不同,可能导致分配不均。
Sticky (粘性)分配策略
- 首次分配时尽量均衡(类似 RoundRobin)。
- 消费者变动时(如重启),尽量保留原有分配,仅调整必要的分区。
- 优点:减少分区迁移(降低 Rebalance 开销)。避免重复消费或数据倾斜。
- 适用场景:消费者组频繁扩缩容(如云环境动态伸缩)。
- 理解“尽量均衡”,7个分区,3个消费者,三个消费者消费的分区数量是3,2,2。和range策略不同的是,range会先将分区排序,sticky则不会
分配策略的选择建议
- 分区数固定,消费者稳定。Range 或 RoundRobin
- 消费者动态变化(如 Kubernetes Pod 扩缩容)。Sticky
- 需要最小化 Rebalance 影响。Sticky
- 自定义分区逻辑(如按业务键分配)。自定义策略
消费者offset的默认维护位置
- kafka消费者offset维护在系统主题__consumer_offsets中,主题里面采用 key 和 value 的方式存储数据。
- key 是 group.id+topic+分区号, value 就是当前 offset 的值。 每隔一段时间, kafka 内部会对这个 topic 进行compact(紧凑),也就是每个 group.id+topic+分区号就保留最新数据。
消费者自动提交offset
- enable.auto.commit 默认值为 true,消费者会自动周期性地向服务器提交偏移量。
- auto.commit.interval.ms定义了消费者偏移量向 Kafka 提交的频率,默认5s。
消费者手动提交offset
- 虽然自动提交offset十分简单便利, 但由于其是基于时间提交的, 开发人员难以把握offset提交的时机。 因此Kafka还提供了手动提交offset的API。
- 提交早了有可能提前消费
- 手动提交offset的方法有两种:
- commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。
- commitAsync(异步提交):发送完提交offset请求后,就开始消费下一批数据了。
- 相同点是, 都会将本次poll() 拉取的一批数据中最高的偏移量提交
- 不同点是,同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试;而异步提交则没有失败重试机制,故有可能提交失败。
指定 Offset 消费
- auto.offset.reset 是 Kafka 消费者的一个重要配置参数,它决定了当消费者组初次启动或要消费的偏移量在Broker上不存在时(例如偏移量已被删除),消费者的行为策略
- auto.offset.reset = earliest | latest | none 默认是 latest。
- earliest:自动将偏移量重置为最早的偏移量, --from-beginning。
- latest(默认值):自动将偏移量重置为最新偏移量
- none:如果未找到消费者组的先前偏移量,则向消费者抛出异常。
指定时间消费
需求:在生产环境中,会遇到最近消费的几个小时数据异常,想重新按照时间消费。例如要求按照时间消费前一天的数据,怎么处理?
offsetsForTimes() 方法,研发写代码实现
数据积压(消费者如何提高吞吐量)
1)如果是Katka消费能力不足,则可以考虑增加Topic的分区数,并且同时提升消费组的消费者数量,消费者数=分区数。(两者缺一不可)
2)如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间<生产速度),使处理的数据小于生产的数据,也会造成数据积压。




浙公网安备 33010602011771号