一、先说核心结论Kafka 不会主动选拔OSR 进入 ISR——但 OSR Follower 满足条件时会自动申请入队。这个过程叫重新同步Re-sync。但这只解决ISR 满员的问题——新的 Leader 必须从现有 ISR 里选OSR 永远不会被选为 Leader除非unclean.leader.election.enabletrue金融场景必须 false。二、先把三个概念拉直概念含义ISRIn-Sync Replicas同步副本集—— 能写、能选 LeaderOSROut-of-Sync Replicas落后副本集—— 能写、不能选 LeaderReplica 列表ISR OSR 所有副本Replica 列表 {R0, R1, R2, R3, R4}↓┌─────────┴─────────┐↓ ↓ISR OSR{R0, R1, R2} {R3, R4}三、ISR 的入队 / 出队机制1.进 ISR 的条件被动入队OSR Follower 必须做到1. 启动后从 Leader 拉取数据2. 持续同步追上 Leader 的 LEOLog End Offset3. 在 replica.lag.time.max.ms 内默认 30s保持同步4. 满足条件 → 自动加入 ISR关键代码KafkaReplicaManager// Kafka 判断 Follower 是否追上 Leaderprivate boolean isFollowerInSync(Replica replica, long highWatermark) {// 1. Follower 最后 fetch 时间long lastFetchTime replica.getLastFetchTimestamp();// 2. 当前时间long now System.currentTimeMillis();// 3. 判断多久没 fetch 了if (now - lastFetchTime replica.lag.time.max.ms) {return false; // 超过阈值 → 踢出 ISR}// 4. Follower 的 LEO 跟 Leader 的差距long followerLEO replica.getLogEndOffset();long leaderLEO leaderReplica.getLogEndOffset();// 5. 差距在允许范围内return followerLEO leaderLEO - maxLag;}2.出 ISR 的条件主动踢出Follower 被踢出 ISR 的两种情况触发条件阈值说明延迟太久replica.lag.time.max.ms30s30s 内没向 Leader 发起 fetch落后太多replica.lag.max.messages4000落后消息数超过 4000已废弃核心机制Leader 周期性检查所有 Follower 的状态。// Kafka 的 ISR 收缩检查简化public void checkISRShrink() {for (Partition partition : partitions.values()) {for (Replica follower : partition.followers()) {// 检查是否掉队if (!isFollowerInSync(follower, partition.hw)) {// 踢出 ISRpartition.removeFromISR(follower);log.info(Follower {} removed from ISR for partition {},follower, partition);}}}}3.重启 Follower 时的自动入队Follower 重启T0s: 启动进程T5s: 加入集群注册到 ControllerT10s: 开始从 Leader 拉取数据T15s: 同步追上LEO 接近 LeaderT30s: 满足 replica.lag.time.max.ms30s → 自动加入 ISR关键点Kafka 不会主动 选拔 OSR——OSR 是自我救赎——满足条件就自动回 ISR。四、大量宕机后的完整时序5 节点集群3 副本假设 ISR{R0(L), R1, R2}阶段 1R1 挂ISR {R0(L), R1, R2} → ISR {R0(L), R2}状态1 个挂可写 ✅2 ≥ min.insync.replicas2阶段 2R2 挂大量宕机开始ISR {R0(L), R2} → ISR {R0(L)}↓R2 启动后被踢出 ISR进入 OSR状态2 个挂min.insync.replicas2 ❌ 不满足↓⚠️ Producer 抛 NotEnoughReplicasException写入失败但数据不丢阶段 3R0Leader也挂极端灾难ISR {R0(L)} → ISR {}Partition 状态unclean.leader.election.enablefalse↓没有 ISR 可以选为 Leader↓Partition 不可用写不了、读不了这时候 OSR 的 R1、R2 在干嘛R1OSR: 启动后从 Leader 拉数据 → 但 Leader 挂了 → 拉不到R2OSR: 同上⚠️ 重要OSR 在 Leader 挂掉期间**无法成为 Leader**⚠️ 除非 unclean.leader.election.enabletrue金融场景必 false阶段 4R1 重启OSR 申请补员T0s: R1 启动T10s: R1 加入集群注册到 ControllerController 检测到 ISR 为空→ 选主失败uncleanfalse无 OSR 可选→ Partition 持续不可用T15s: R1 尝试从 Leader 拉数据 → Leader 不存在 → 失败R1 进入等待 Leader状态T30s: R1 仍无法拉数据 → 无法入 ISRPartition 仍不可用OSR 没法自动补员 ISR——必须等 Leader 复活。阶段 5R0Leader恢复灾难恢复T0s: R0 启动T10s: R0 注册到 ControllerController 检测到 R0 是上一个 Leader任期最高→ R0 直接成为 Leader不重新选举→ ISR {R0(L)}先只有 R0 一个→ Partition 可写但 min.insync.replicas2 不满足T15s: R0 开始接受写入R1、R2 还在 OSR开始从 R0 拉数据T30s: R1 追上 R0 的 LEO → 自动加入 ISRISR {R0(L), R1} → min.insync.replicas2 满足 ✅↓Producer 自动恢复写入T60s: R2 也追上 → ISR {R0(L), R1, R2}完全恢复正常总故障时间约 1-2 分钟取决于机器启动 数据同步速度五、关键问题OSR 能不能跳过 Leader 直接补员答案不能原因Kafka 的设计哲学Kafka 的选举规则- 新 Leader 必须在 ISR 列表里- ISR 已经同步的副本 数据最新的副本- OSR 数据落后的副本数据落后意味着- 上次 Leader 挂掉时OSR 可能没收到所有数据- 让 OSR 当 Leader 可能丢数据这就是unclean.leader.election.enable的核心价值配置OSR 能否当 Leader金融场景false推荐❌ 不能✅ 必须这个true✅ 能但可能丢数据❌ 禁用六、5 个实战关键点1.OSR 的自救机制OSR Follower 必须做到 3 件事才能回 ISR1. 不停地向 Leader 发起 FetchRequest哪怕 Leader 不存在2. 一旦 Leader 恢复立刻追数据3. 满足 replica.lag.time.max.ms 的同步窗口Kafka 不会主动通知OSR 来同步OSR 必须自我驱动2.replica.lag.time.max.ms是关键参数# 默认 30sreplica.lag.time.max.ms30000# 影响# - 设太短如 5s→ 网络抖动就会被踢出 ISR → ISR 频繁收缩# - 设太长如 5min→ 真正掉队的 Follower 长时间停留在 ISR# - 推荐30s同机房/ 60s跨机房3.大量宕机后的补救操作# 1. 监控 ISR 收缩kafka-topics.sh --describe --bootstrap-server localhost:9092 --topic mytopic# 看每个 Partition 的 ISR 列表# 输出示例# Topic: mytopic Partition: 0 Leader: 1 Replicas: 1,2,3 Isr: 1# ↑ ↑# Leader 在 ISR 只有 1 个# 2. 手动触发副本重新同步kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \--reassignment-json-file reassign.json --execute# 3. 增加副本数扩容kafka-reassign-partitions.sh --bootstrap-server localhost:9092 \--reassignment-json-file expand.json --execute4.避免Leader 孤立极端情况Leader 是唯一 ISR 成员- 其他 Follower 全部 OSR 或挂掉- min.insync.replicas 满足如果只配 1- 但其他副本追不上来- 单点风险极大✅ 解决min.insync.replicas2 合理副本数5.配置replica.fetch.min.bytes加速同步# Follower 拉数据的最小字节数replica.fetch.min.bytes1# 默认 1立即返回# 设大会延迟同步 → OSR 追上更慢# ✅ 推荐保持 1紧急恢复时优先速度七、面试话术Kafka ISR 大量宕机后的补员机制是 Follower 自我驱动的不是 Kafka 主动选拔——ISR 出队条件Follower 在replica.lag.time.max.ms30s内没向 Leader 拉数据 → 被踢出 ISR进入 OSR。ISR 入队条件OSR Follower 持续追上 Leader 的 LEO 满足时间窗口 →自动重新加入 ISR。关键限制OSR 永远不能当 Leader除非unclean.leader.election.enabletrue金融场景必 false——所以补员 ISR ≠ 能立刻选主。如果 Leader 也挂了OSR 必须等 Leader 恢复才能恢复服务。实战应对监控 ISR 收缩告警、replica.lag.time.max.ms调合理值30s、min.insync.replicas2replication.factor3uncleanfalse三件套必配。八、和 Raft 的对比加深理解场景RaftKafka ISR节点挂掉重新选举Term 自增Controller 从 ISR 选新 Leader落后节点能当 Leader 吗❌ 不行日志旧❌ 不行不在 ISR落后节点能补员吗通过选举追日志通过追数据→自动入 ISR拒绝服务条件多数派不可用ISR min.insync.replicas本质相同都是多数派数据最新优先原则。九、生产事故案例背景Kafka 5 节点集群3 副本min.insync.replicas2事故- Broker-3 磁盘写满zookeeper 监控漏报- Broker-3 的 Follower 进程自动停止- Broker-3 节点从 ISR 中被踢出应对- 监控告警ISR 收缩- Broker-3 扩容磁盘后重启- Follower 进程从 Leader 追数据- 30s 后自动加入 ISR- 全程 2 分钟业务无感知 ✅教训✅ ISR 收缩监控必须 P0✅ 磁盘使用率告警 80%✅ Follower 重启后会自动补员——Kafka 设计得很好十、一句话总结Kafka ISR 没有主动选拔机制——OSR 是自我救赎满足条件自动入 ISR但永远不能当 Leader金融场景必配uncleanfalse。大量宕机后的恢复时序先复活 Leader → ISR 重新建立 → OSR 追数据 → 自动补员 ISR——整个过程是Follower 自我驱动Kafka 不主动介入。