Kafka 副本机制
副本机制是 Kafka 实现高可用和数据持久化的核心机制。本章将详细介绍 Kafka 副本的工作原理、同步机制和配置优化。
副本概述
什么是副本?
Kafka 通过将分区数据复制到多个 Broker 来实现高可用。每个分区可以有多个副本,分布在不同 Broker 上,当某个 Broker 故障时,其他副本可以继续提供服务。
副本角色
| 角色 | 说明 | 职责 |
|---|---|---|
| Leader | 主副本 | 处理所有读写请求,负责数据同步 |
| Follower | 从副本 | 被动复制 Leader 数据,不处理客户端请求 |
| ISR | 同步副本集合 | 与 Leader 保持同步的副本,有资格成为新 Leader |
为什么需要副本?
- 高可用:Broker 故障时,副本可以接管服务
- 数据持久化:多副本存储,避免单点数据丢失
- 负载均衡:Follower 可以承担部分读请求(通过 Follower Fetching)
- 容灾:跨机房部署副本,实现异地容灾
副本同步机制
同步原理
关键概念
LEO(Log End Offset):日志末端偏移量,表示下一条要写入的消息偏移量。每个副本都有自己的 LEO。
HW(High Watermark):高水位,表示已成功复制到所有 ISR 副本的消息偏移量。消费者只能看到 HW 之前的消息。
- 绿色:HW 之前,消费者可见
- 红色:HW 之后,尚未完全复制,消费者不可见
副本同步流程
- Follower 发送 Fetch 请求:携带当前 Fetch Offset
- Leader 处理 Fetch 请求:从日志中读取数据返回
- Follower 写入日志:更新本地 LEO
- Leader 更新 HW:取所有 ISR 副本 LEO 的最小值
- Follower 更新 HW:从下一次 Fetch 响应中获取
副本 Fetcher 配置
# Follower 拉取配置
replica.fetch.max.bytes=1048576
replica.fetch.min.bytes=1
replica.fetch.wait.max.ms=500
replica.fetch.backoff.ms=1000
replica.socket.timeout.ms=30000
replica.socket.receive.buffer.bytes=65536
ISR(In-Sync Replicas)
什么是 ISR?
ISR(In-Sync Replicas)是当前与 Leader 保持同步的副本集合。只有 ISR 中的副本才有资格成为新的 Leader。
ISR 判定条件
副本是否在 ISR 中,取决于是否能在配置时间内追上 Leader:
# 判定副本是否在 ISR 中的时间阈值(默认 30s)
replica.lag.time.max.ms=30000
如果 Follower 在 replica.lag.time.max.ms 时间内没有发送 Fetch 请求或未能追上 Leader 的 LEO,则会被踢出 ISR。
ISR 动态调整
ISR 收缩:当 Follower 滞后时,Leader 将其从 ISR 中移除,并通知 Controller。
ISR 扩张:当 Follower 追上 Leader 时,Leader 将其加入 ISR,并通知 Controller。
min.insync.replicas
min.insync.replicas 定义了 ISR 中必须保持的最小副本数:
# 生产环境建议设置为 2
min.insync.replicas=2
作用:
- 配合
acks=all使用,确保消息写入足够多的副本 - 如果 ISR 数量少于该值,生产者会收到
NotEnoughReplicasException - 提高数据可靠性,牺牲一定的可用性
消息确认机制(acks)
acks 配置选项
// acks=0:不等待确认
props.put("acks", "0");
// 最高吞吐,最低可靠
// 适用于可丢失消息的场景(如日志收集)
// acks=1:Leader 确认
props.put("acks", "1");
// 默认配置
// Leader 写入成功即返回,可能丢失数据(Leader 故障且 Follower 未同步)
// acks=all(或 -1):所有 ISR 确认
props.put("acks", "all");
// 最高可靠,稍低吞吐
// 配合 min.insync.replicas 使用
可靠性与性能权衡
| acks | 可靠性 | 吞吐量 | 延迟 | 数据丢失场景 |
|---|---|---|---|---|
| 0 | 最低 | 最高 | 最低 | Broker 故障即丢失 |
| 1 | 中等 | 高 | 低 | Leader 故障且 Follower 未同步 |
| all | 最高 | 较低 | 较高 | ISR 全部故障 |
推荐配置
// 高可靠配置
Properties highReliabilityProps = new Properties();
highReliabilityProps.put("bootstrap.servers", "localhost:9092");
highReliabilityProps.put("key.serializer", "StringSerializer");
highReliabilityProps.put("value.serializer", "StringSerializer");
// 确保消息复制到多个副本
highReliabilityProps.put("acks", "all");
highReliabilityProps.put("min.insync.replicas", "2");
// 启用幂等性和重试
highReliabilityProps.put("enable.idempotence", "true");
highReliabilityProps.put("retries", "3");
highReliabilityProps.put("max.in.flight.requests.per.connection", "5");
Leader 选举
选举触发条件
- Leader 所在 Broker 故障:Controller 检测到 Broker 不可用
- 手动触发首选副本选举:恢复初始 Leader 分布
- 手动指定 Leader:管理员强制指定
选举策略
正常选举(Clean Election):
- 从 ISR 中选择新的 Leader
- 保证数据不丢失
- ISR 为空时无法选举
Unclean 选举:
- 当 ISR 为空时,允许从非 ISR 副本中选举 Leader
- 可能导致数据丢失
- 需要显式启用:
unclean.leader.election.enable=true
手动触发选举
# 首选副本选举
kafka-leader-election.sh --bootstrap-server localhost:9092 \
--topic my-topic \
--partition 0 \
--election-type preferred
# Unclean 选举(谨慎使用)
kafka-leader-election.sh --bootstrap-server localhost:9092 \
--election-type unclean \
--topic my-topic \
--partition 0
副本分配策略
自动分配
Kafka 自动分配副本到 Broker,遵循以下原则:
- 副本分散:同一分区的副本分布在不同 Broker
- Leader 均衡:Leader 均匀分布到各 Broker
- 机架感知:考虑机架位置,跨机架分布副本(如果配置)
手动分配
创建主题时可以指定副本分布:
# 指定副本分配
kafka-topics.sh --create \
--topic my-topic \
--bootstrap-server localhost:9092 \
--replica-assignment "0:1:2,1:2:0,2:0:1"
# 分区0: 副本在 Broker 0,1,2
# 分区1: 副本在 Broker 1,2,0
# 分区2: 副本在 Broker 2,0,1
机架感知配置
# Broker 配置
broker.rack=rack1
# 创建主题时会考虑机架
# 同一分区的副本会分布在不同机架
副本重分配
# 1. 创建重分配计划
cat > reassign.json << EOF
{
"version": 1,
"partitions": [
{"topic": "my-topic", "partition": 0, "replicas": [1,2,3]},
{"topic": "my-topic", "partition": 1, "replicas": [2,3,0]}
]
}
EOF
# 2. 执行重分配
kafka-reassign-partitions.sh --execute \
--reassignment-json-file reassign.json \
--bootstrap-server localhost:9092
# 3. 验证进度
kafka-reassign-partitions.sh --verify \
--reassignment-json-file reassign.json \
--bootstrap-server localhost:9092
副本监控
关键指标
# 查看副本状态
kafka-topics.sh --describe \
--topic my-topic \
--bootstrap-server localhost:9092
输出说明:
Topic: my-topic Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1,2,3
↑ ↑
AR (Assigned) ISR (In-Sync)
常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| ISR 数量减少 | Follower 滞后或故障 | 检查网络和磁盘性能 |
| 副本不同步 | 复制速度慢 | 调整 replica.fetch.max.bytes |
| Leader 不均衡 | Broker 重启后未恢复 | 执行首选副本选举 |
| UnderReplicated | 副本数少于期望值 | 检查 Broker 状态和网络 |
监控脚本
#!/bin/bash
# 检查副本状态
echo "=== 检查 Under-Replicated 分区 ==="
kafka-topics.sh --describe \
--under-replicated-partitions \
--bootstrap-server localhost:9092
echo ""
echo "=== 检查离线分区 ==="
kafka-topics.sh --describe \
--unavailable-partitions \
--bootstrap-server localhost:9092
JMX 指标
| 指标 | 说明 |
|---|---|
UnderReplicatedPartitions | 复制不足的分区数 |
IsrShrinksPerSec | ISR 收缩速率 |
IsrExpandsPerSec | ISR 扩张速率 |
LeaderElectionRate | Leader 选举频率 |
ReassignmentPercentCompleted | 重分配完成百分比 |
故障恢复
Broker 故障恢复
恢复后同步
当故障 Broker 恢复后:
- Broker 重新上线,成为 Follower
- 从新 Leader 同步数据,截断 HW 之后的消息
- 追上 Leader 后,重新加入 ISR
- 可能成为 Leader(如果是首选副本)
数据恢复
# 检查日志完整性
kafka-dump-log.sh --files /var/lib/kafka/data/topic-0/00000000000000000000.log \
--print-data-log
# 检查副本一致性
kafka-replica-verification.sh \
--broker-list "0:9092,1:9092,2:9092" \
--topic-white-list ".*"
最佳实践
副本配置
# 生产环境推荐配置
default.replication.factor=3
min.insync.replicas=2
unclean.leader.election.enable=false
# 副本同步配置
replica.lag.time.max.ms=30000
num.replica.fetchers=2
容量规划
| 场景 | 副本数 | min.insync.replicas | 特点 |
|---|---|---|---|
| 开发测试 | 1 | 1 | 最小资源占用 |
| 一般生产 | 2 | 1 | 平衡可用性和成本 |
| 高可用生产 | 3 | 2 | 高可用,容忍 1 个节点故障 |
| 金融级别 | 3+ | 2+ | 极高可用性 |
监控告警
建议设置以下告警:
- ISR 数量变化告警
- UnderReplicated 分区告警
- Leader 选举频繁告警
- 副本同步延迟告警
小结
- 副本机制是 Kafka 高可用的基础,通过多副本存储保证数据安全
- Leader 处理读写请求,Follower 被动复制数据
- ISR 是与 Leader 同步的副本集合,决定哪些副本可以成为 Leader
- acks 配置影响消息可靠性,
acks=all配合min.insync.replicas实现最高可靠性 - 合理配置副本数和 min.insync.replicas 保证高可用
下一步
接下来让我们学习 Kafka Streams 流处理框架。