有一年冬天凌晨值班的兄弟发了张截图过来Topic: order-event Partition: 7 Leader: -1 Replicas: 3,4,5 Isr:。他说消费端卡住了重启消费者也没用。这就是典型的Kafka partition leader-1不是消费者出问题也不是网络抖动而是这个分区在当前集群里根本没有 leader客户端连“应该找谁读”都回答不了。很多团队第一次遇到这个提示第一反应是重启 broker、重启消费者结果是白折腾好几个小时。这篇就把我处理这类问题的完整思路摊开讲从leader-1这个数字在元数据里代表什么、到四类常见成因怎么快速分类、再到具体该按哪个开关、跑哪条命令、什么时候该放弃抢救直接重建。修复方案整理这个题目看着像文档归档其实里面有几处细节如果不点破照着网上搜到的片段去操作很容易把“分区不可用”升级成“数据丢失”。1. leader-1 不是“分区坏了”而是“没人认领”1.1 把 --describe 那一行拆开看先用最朴素的命令把现场拍下来kafka-topics.sh --bootstrap-server broker1:9092 --describe --topic order-event输出里每一行对应一个分区关键在于四个字段的组合关系。Leader是当前承担读写的主副本所在的 broker id正常是个非负整数Replicas是这个分区的完整副本集合也就是 ARAssigned Replicas是创建分区时分配方案定下来的正常情况下不会变Isr是当前与 leader 保持同步的副本集合是动态的新版还会多出OfflineReplicas直接列出哪些副本所在 broker 已经联系不上。Leader: -1配上Isr:后面空空如也意思非常明确这个分区没有任何一个副本有资格被选为 leadercontroller 在元数据里把一个“不存在的 broker id”写进了 leader 字段。Kafka 里 -1 不是错误码是一个合法的“无”的表示跟__consumer_offsets里没有 offset 时的 -1 是同一个用法。值得单独说一句的是Leader: -1和Isr: 3,4,5这种组合几乎不会同时出现。要是 ISR 里还有人controller 早就把其中一个提成 leader 了。所以看到 -1基本可以断定 ISR 是空的或者 controller 压根没收到足够信息来做决定。1.2 四个字段之间的因果链理解这条链比记命令重要得多。副本集合Replicas是静态的分配结果它决定了“理论上这个分区应该有几个人在管”Isr是由副本向 leader 拉取数据、以及 broker 心跳汇总出来的动态结果leader 是从Isr里按某种规则挑出来的那个而OfflineReplicas是“连心跳都没了”的那部分。链路的断点不同症状就不同。如果Replicas里的人全掉线了Isr自然空leader 也选不出来如果人都在线但都跟不上 leader 的进度会被踢出Isr同样选不出来如果 controller 自己状态不对它可能会把 leader 字段写成 -1 却迟迟不更新。这三种情况的修复动作完全不一样所以第一步永远是分清是哪种而不是急着敲命令。我个人的习惯是先跑一条--describe --unavailable-partitions它会把所有“没有可用 leader”的分区一次性列出来kafka-topics.sh --bootstrap-server broker1:9092 --describe --unavailable-partitions这条命令的输出规模直接决定了你面对的是单个分区的偶发问题还是整个集群某类系统性故障。如果只有一两个 topic 的几个分区中招多半是局部资源问题如果几十个 topic 大面积刷屏那就往 broker 批量掉线、controller 异常、或者元数据损坏的方向想。1.3 分区状态机和 leader 的缺席形态Kafka 的分区在 controller 视角下有一组状态虽然不直接暴露给使用者但决定了 leader 什么时候会被填上。当一个分区的所有副本都进入Offline分区处于OfflinePartition状态当 ISR 非空但 leader 尚未选出属于OnlinePartition的中间态当选出的 leader 因为某种原因失效会退回到OfflinePartition或者NonExistentPartition。这个状态机有个特点大部分状态转移是由事件驱动的而不是定时轮询。也就是说如果触发条件消失了比如 broker 重新上线controller 未必会立刻感知并重算有些版本要等到下一次副本状态变更、或者 controller 重新加载元数据时才会修正。这解释了一个很常见的现象你把挂掉的 broker 拉起来了kafka-broker-api-versions.sh也能连上但--describe里还是Leader: -1过几分钟才自己变好。别慌这是正常收敛过程不是修复失败。2. 四类成因各自的指纹不一样2.1 副本集全部离线最直白的一种Replicas: 3,4,5而Isr:为空同时kafka-broker-api-versions.sh连 3、4、5 都超时这就是副本集全部离线。常见触发场景有三个机器物理故障、broker 进程被 OOM killer 干掉、以及磁盘写满导致 broker 主动关闭Kafka 对日志目录写入失败极其敏感log.dirs下任何一块盘出问题都可能让整个 broker 退出。判断依据很硬ls /brokers/idsZK 模式或者kafka-metadata-quorum.sh加 broker 注册信息KRaft 模式里根本没有这几个 id。这种情况下 leader 为 -1 是“正确”的Kafka 只是如实反映了事实没有任何元数据错乱。修复路径也是四类里最清晰的让 broker 回来。有个细节容易被忽略broker 回来之后如果它是靠log.dirs里的数据恢复的副本内容可能已经落后于其他副本如果其他副本还在或者它自己就是唯一的数据持有者。前者会被暂时排除在 ISR 之外等追上进度再入列后者会直接成为 leader 但带着旧数据这就要靠后面的 unclean 选举章节来判断风险了。2.2 ISR 被清空副本活着但没资格选这是最迷惑人的一类。三个 broker 都活着进程正常端口能连但Isr:就是空的。日志里往往能找到这样的行Shrinking ISR for partition order-event-7 from (3,4,5) to ()为什么会缩到空通常是副本同步跟不上被 leader 或者 controller 判定为“不再同步”而踢出。触发原因包括某个 broker 的磁盘 IO 被打爆大量写入把 IO 队列堵死、分区数据量过大导致追赶超时replica.lag.time.max.ms默认 30 秒在数据量大的分区上很容易超、broker 与 leader 之间的网络抖动持续超过阈值。这条链要特别注意ISR 收缩是逐个进行的。假设一开始 leader 是 34 和 5 因为慢先后被踢然后 3 自己也因为一次 Full GC 或者 IO 卡顿超过了replica.lag.time.max.mscontroller 收不到 3 的心跳确认最终把 3 也踢掉ISR 就空了。一旦空了unclean.leader.election.enablefalse的默认配置下谁都不能当 leader分区直接停摆。这里有个和min.insync.replicas的区别必须讲清楚两者经常被混为一谈。min.insync.replicas作用在写入侧当acksall且 ISR 数量小于这个值时生产端会收到NotEnoughReplicasException但分区的 leader 还在消费端还能正常读。而 ISR 清空导致的leader-1是读也读不了。前者是“不让你写”后者是“这个分区没了”。诊断的时候如果生产端报错但消费端正常那是前者如果连消费都卡住、--describe显示 -1那才是后者。2.3 控制器视角与实际状态脱节第三类比较隐蔽broker 都活着数据也都在但 controller 脑子里的账本和实际情况对不上。典型表现是 controller 日志里反复出现LeaderAndIsr相关的异常或者Controller 0 epoch频繁切换。ZK 模式下这往往是/controller这个临时节点残留旧 controller 挂了但节点没来得及删新 controller 起来后两边抢导致状态混乱。KRaft 模式下则是 controller quorum 里 leader 选举异常需要看kafka-metadata-quorum.sh describe --status输出的CurrentVoters、LeaderId和HighWatermark是否合理。还有一类是元数据本身被外部写坏了。比如有人直接在 ZK 里改了/brokers/topics/topic/partitions/p/state节点或者用旧版本工具做过操作节点内容和 broker 实际状态对不上controller 一读就出现 leader 指向一个不存在的 id。这种情况日志里通常伴随Invalid leader、Unknown broker之类的关键字。判断这类的关键信号是重启单个 broker 无效重启 controller 有效。如果你的操作历史里重启 broker 没用、但把 controller 切了一下就好了基本可以确定是控制面问题而非数据面问题。2.4 副本分配本身不可能满足最后一类属于“设计阶段就埋下的雷”。最典型的是Replicas里包含了已经被退役、或者从来没启动过的 broker id。比如早期规划了 5 台 broker副本分配使用了 id 1 到 5后来缩到 3 台id 4 和 5 已经下线但没有任何一次重分配把数据挪走。此时只要这三个副本里在线的那个比如 id 3也出点问题剩余两个永远回不来的 id 就成了 ISR 的拖累。还有一种更常见Replicas数量大于存活 broker 数量。三台机器的集群上建了副本数 5 的 topicKafka 允许这样做创建时不校验但一旦有 broker 下线就会出现多个副本落在同一台机器上或者落在不存在的机器上回收过程非常痛苦。这类问题的指纹是--describe里的Replicas集合里有 id 明显超出了当前ls /brokers/ids的范围而且这个状态持续了很久不是刚发生。这时候修复方向就不是“救”而是“改分配”属于第 4 章的重分配范畴。3. 排查顺序先看谁活着再看谁没说话3.1 基础检查清单与对应命令我把五分钟内必须跑完的检查固化成一张表按顺序来每一步的结果都能排除掉一批可能性顺序检查项命令关键判读1分区现状快照kafka-topics.sh --bootstrap-server b:9092 --describe --unavailable-partitions是单点还是区域性2在线 broker 列表kafka-broker-api-versions.sh --bootstrap-server b:9092副本集里的 id 是否全在3日志目录健康度kafka-log-dirs.sh --bootstrap-server b:9092 --describe --topic-list order-event每个副本的 size 和 offsetLag4controller 是谁ls /brokers/ids找controller或 KRaft 下kafka-metadata-quorum.sh describe --statuscontroller 是否唯一且稳定5副本分配是否畸形--describe里对比Replicas与在线 id是否含有已下线 idkafka-log-dirs.sh这一步很多人跳过其实信息量很大。它能告诉你每个 broker 上这个分区目录的实际大小、offsetLag该副本落后 leader 的量、以及isFuture标记。如果某个副本size和别的副本差一个数量级那它肯定进不了 ISR如果某个 broker 上压根没有这个分区的目录那说明副本数据丢了比离线更严重。kafka-log-dirs.sh --bootstrap-server broker1:9092 --describe --topic-list order-event输出是 JSON找partitions数组里对应分区号的那一项看replicaInfos里的size和offsetLag字段。3.2 日志里必须捞出来的几行命令只能看到“现在是什么样”日志才能告诉你“为什么会变成这样”。controller 的server.log是首要目标几个必须搜的关键字grep -E Shrinking ISR|Not able to elect|LeaderAndIsr|Broker .* is now the leader|Controller .* epoch controller.logShrinking ISR for partition xxx to ()是 ISR 清空的直接证据注意它前面几行通常会说明原因比如Replica 4 is out of sync或者broker 4 is now fenced。Not able to elect a leader for partition xxx because the ISR is empty and unclean leader election is disabled这句话出现等于直接告诉你代码路径走到了“因为开关关着所以不选”修复方向就是第 4.2 节。broker 侧日志要搜ReplicaFetcherThread相关的错误以及kafka.log.Log里的IOException、Disk error、No space left on device。磁盘问题几乎都会在日志里留下明确证据比看监控更靠得住。还有一个容易漏的点检查meta.properties。每个 broker 的数据目录里都有这个文件记录broker.id和cluster.id。如果有人改过server.properties里的broker.id但目录里还是旧值broker 启动会失败或者用旧 id 注册导致元数据里出现“幽灵 broker”。这个坑我后面第 6 章会展开讲。3.3 把时间线对齐再下结论排查到这一步手里有一堆日志和命令输出但还不是下结论的时候。最容易犯的错是看到一个Shrinking ISR就认定是那个 broker 慢实际上真正的原因可能是上游的磁盘 IO 抖动或者同一宿主机上的其他进程抢了资源。我的做法是把三个时间点对齐分区最后一次 leader 变化的时刻、broker 掉线或重启的时刻、以及第一个Shrinking ISR出现的时刻。如果第一个 ISR 收缩出现在 broker 重启之前那说明是同步问题导致的退出broker 重启只是结果或者无关动作如果出现在之后那大概率是重启过程中数据没写完或者追赶超时。对齐时间线还有一个好处能判断这次是“一次性事件”还是“周期性发作”。如果过去一周里同一个分区反复出现 ISR 清空那不管你这次怎么修很快会复发必须往配置或者容量方向找根因而不是修完了事。4. 修复动作按成因分流4.1 先把 broker 拉回来但别用错误的方式重启对于第 2.1 节那种副本集全部离线的情况动作很直接把 broker 拉起来。但“拉起来”这三个字里有讲究。第一确认磁盘没问题再启动。如果 broker 是因为No space left on device退出的空间没腾出来就重启它会立刻再挂一次而且每一次启动都会在日志目录里留下需要清理的残留文件反复几次之后日志恢复会变得很慢。先用df -h和du看看是谁把盘吃满了log.retention.hours是不是配得太长或者是不是有某个 topic 的流量突然暴涨。第二不要在没有确认的情况下同时启动多个 broker。多台同时启动会同时触发日志恢复Log recovery大量磁盘随机读会把整个机器的 IO 打满反而拖长恢复时间。如果集群规模不大一台一台来看到kafka-broker-api-versions.sh能连上、--describe里Isr开始有人了再启动下一台。第三启动之后不要立刻认定失败。前面说过状态机是事件驱动的broker 注册成功不等于 controller 已经重算完成。我的经验是给它 1 到 2 分钟期间可以观察 controller 日志有没有Broker 3 is now the leader for partition order-event-7这类记录。如果 5 分钟以上仍然Leader: -1而 broker 明明在线那才是真的有问题往下走 unclean 选举或者重分配的路径。如果 broker 已经彻底起不来磁盘物理损坏、机器报废那就要走另一个判断剩余副本如果还有能不能撑起这个分区。如果Replicas: 3,4,5里 3 报废4 和 5 都活着那 leader 会自动在 4、5 之间选出来你只需要尽快把 3 从分配里挪走。挪走的过程是重分配见 4.3。4.2 unclean 选举开关在哪按、按完为什么没反应当 ISR 为空、所有副本都落后、而你又必须让分区恢复可用时唯一的办法是unclean.leader.election.enable。这个名字里的“unclean”是准确的它允许从 ISR 之外的副本里选一个当 leader代价是这个副本的数据可能落后选它等于主动放弃它没同步到的那部分数据。这个开关有三层作用域优先级从高到低是topic 级、broker 级、全局server.properties。实操中我推荐优先用 topic 级因为影响面可控kafka-configs.sh --bootstrap-server broker1:9092 \ --alter --entity-type topics --entity-name order-event \ --add-config unclean.leader.election.enabletrue如果版本较老topic 级配置是 2.4 之后才支持的只能退到 broker 级kafka-configs.sh --bootstrap-server broker1:9092 \ --alter --entity-type brokers --entity-name 0 \ --add-config unclean.leader.election.enabletrue这里有个非常容易踩的坑broker 级配置只有当那台 broker 是 controller 时才起作用。因为 unclean 选举的决定是 controller 做出的它读的是自己的配置。如果你把开关设在了 id 为 4 的 broker 上但 controller 是 id 为 0那开关等于没设。所以我一般建议要么设 topic 级随元数据下发不依赖谁当 controller要么直接确认当前 controller 是谁在它上面设。设完开关之后第二个坑来了不一定会立刻触发选举。前面提过状态转移靠事件驱动光改配置不改变任何副本状态controller 可能根本没有重算的动机。这时候可以手动触发一次kafka-leader-election.sh --bootstrap-server broker1:9092 \ --election-type UNCLEAN --topic order-event --partition 7这条命令走的是 Admin API直接要求 controller 对这个分区做一次 unclean 选举。执行成功后会返回Successfully completed leader election。如果返回ElectionNotNeededException通常是开关没生效或者 ISR 其实非空返回PreferredLeaderNotAvailableException则说明这个参数类型用错了。在按下这个开关之前有几件事必须先想清楚。第一确认这个 topic 的数据能不能丢。如果它是订单、账务这类不能容忍缺失的数据正确做法是等落后的副本追上而不是 unclean 选举。第二确认落后的副本落后多少。用kafka-log-dirs.sh或kafka-run-class.sh kafka.tools.GetOffsetShell对比各副本的 log end offset如果差距是几百条消息牺牲的代价小如果差距是几十万条那等于把这个分区最近一段时间的数据全扔了。第三选完之后记得把开关关回去这个我在第 6 章有专门的血泪教训。4.3 副本重分配与限流参数的取舍如果分区的问题本质是“分配方案不合理”第 2.4 节或者你想把报废 broker 上的副本迁走就要用重分配工具。完整流程分三步每步都不能省。第一步生成计划。准备一个描述要迁移哪些 topic 的 JSON{topics:[{topic:order-event}],version:1}kafka-reassign-partitions.sh --bootstrap-server broker1:9092 \ --topics-to-move-json-file topics.json \ --broker-list 1,2,3 --generate这条命令会输出两部分当前的分配方案一定要保存下来回滚时要用和建议的新方案。新方案是工具自动算的会尽量均衡但不保证符合你的机架感知要求所以建议人工审一遍再执行。第二步执行kafka-reassign-partitions.sh --bootstrap-server broker1:9092 \ --reassignment-json-file plan.json --execute --throttle 52428800--throttle单位是字节/秒这里给的是 50 MB/s。这个参数是重分配里最需要权衡的地方不设限流迁移流量会把正常的读写 IO 挤爆表现为生产端延迟飙升、ISR 又开始收缩设得太小几 TB 的数据要迁好几天期间集群一直处于部分副本状态风险窗口太长。我的经验值是单块盘的顺序写能力的三分之一左右机械盘上大概 30 到 50 MB/sNVMe 上可以放到 200 MB/s 以上同时要盯住kafka.server:typeBrokerTopicMetrics,nameBytesInPerSec有没有被挤压。第三步验证kafka-reassign-partitions.sh --bootstrap-server broker1:9092 \ --reassignment-json-file plan.json --verify只有当所有分区的状态都是completed才算迁移完成。中途如果想取消用--cancel但要注意--cancel只是停止后续迁移已经迁完的不会回滚所以要恢复原状还得手动执行一次旧方案的 execute。重分配期间有个必须注意的点不要在迁移过程中再叠加其他大动作。我见过有人在迁数据的同时改num.partitions、调整 retention、还顺手重启了一个 broker结果 controller 元数据更新队列积压整个集群卡住十几分钟。4.4 数据可丢场景下的重建路径还有一类情况救的成本远高于重建。典型是分区数据本身已经不重要日志类、临时缓存类或者所有副本的数据目录都确认损坏了磁盘物理故障。这时候最快的方式是直接删了重建。kafka-topics.sh --bootstrap-server broker1:9092 --delete --topic order-event kafka-topics.sh --bootstrap-server broker1:9092 --create --topic order-event \ --partitions 12 --replication-factor 3 \ --config min.insync.replicas2但删除 topic 这件事在 Kafka 里不是同步的。发出--delete之后topic 会被标记为marked for deletion实际清理是异步的delete.topic.enable没开的话压根不会删。删完之后立刻用同名重建可能会返回TopicAlreadyExistsException。稳妥的做法是删完用--list确认 topic 已经不在列表里再执行创建。如果不想删整个 topic只是某个分区卡住了也可以考虑用kafka-topics.sh --alter调整分区数——但要注意增加分区数不会改变已有分区的分布那个 -1 的分区还是 -1。这条路只在“分区数太少导致单分区压力过大”的场景下有意义。5. 修好之后必须做的三件事5.1 验证不能只看 leader 变正数Leader从 -1 变成 3很多人就收工了。实际上这只是第一步。我会做三项验证第一看 ISR 是否完整。Isr: 3但Replicas: 3,4,5意味着只有一个副本可用另外两个还在追赶。这时候集群的容错能力是零再挂一台就彻底完了。正常状态应该是Isr和Replicas集合一致。第二用生产消费实测。起一个 console consumer 从头或者从某个 offset 读一批数据再用 console producer 写一批确认读写都通。只看--describe有时候会骗人元数据恢复了但底层的日志段还有问题的情况是存在的。第三检查是否有数据空洞。如果刚才做过 unclean 选举或者副本重建leader 的 log start offset 可能已经跳了一段。用kafka-run-class.sh kafka.tools.GetOffsetShell看每个分区的起始和结束 offset对比修复前后的值如果发现 start offset 突然变大说明有数据被截断了得让下游知道。kafka-run-class.sh kafka.tools.GetOffsetShell \ --bootstrap-server broker1:9092 --topic order-event --time -2--time -2返回最早 offset--time -1返回最新 offset。5.2 把配置层的隐患堵上每次处理完这类故障我都会回头看一遍几个配置因为绝大部分leader-1最终都能追溯到配置不合理配置项常见问题值建议值原因replication.factor1 或 232 的时候挂一台就进入临界态min.insync.replicas12配合acksall才能保证不丢unclean.leader.election.enabletrue被误开过false除非明确接受丢数据replica.lag.time.max.ms1000030000太小会导致频繁被踢出 ISRnum.recovery.threads.per.data.dir12 到 4影响重启后的恢复速度log.retention.hours720 以上按业务定太长会吃满磁盘间接引发退出重点说replica.lag.time.max.ms。这个值的含义是“副本多久没追上 leader 就被认为不同步”。默认 30 秒有些团队为了降低延迟把它改成 10 秒甚至 3 秒结果在流量高峰期一次正常的 Full GC 或者一次磁盘刷盘抖动就能让副本被踢出 ISR反复折腾几次 ISR 就空了。如果你的集群有大批量写入的场景宁可把这个值调大一点牺牲一点故障感知速度换取稳定性。5.3 下一次告警前应该有的观测项被动等告警不好主动监控更靠谱。这类故障最有效的三个前兆指标UnderReplicatedPartitions值是副本数不足的分区数量。连续大于 0 且持续上升说明有副本在持续掉队是 ISR 即将清空的早期信号。这个指标在 JMX 里的路径是kafka.server:typeReplicaManager,nameUnderReplicatedPartitions。OfflinePartitionsCount直接就是 leader-1 的分区数量。这个值一旦从 0 变成非 0说明已经有分区不可用了。它比UnderReplicated严重一个量级可以直接挂电话告警。IsrShrinksPerSec 和 IsrExpandsPerSec这两个要一起看。如果收缩的频率明显高于扩张说明同步能力出了问题如果两者都接近 0说明集群很稳定。只看收缩不看扩张会被正常的节点维护误导。除了指标还建议给kafka-topics.sh --describe --unavailable-partitions加一个定时任务每分钟跑一次输出非空就报警。虽然比 JMX 粗糙但它能直接给出是哪几个分区省掉了从指标到具体对象的定位时间。6. 几个真实踩过的坑6.1 在 ISR 为空时把 controller 重启了有次集群出现大面积Leader: -1当时判断是元数据错乱直接重启了 controller。结果重启之后情况更糟原本还有一两个分区能读到数据重启后全部不可用而且卡了将近十分钟才恢复。事后复盘才想明白问题在哪。controller 重启会触发一次完整的元数据加载加载过程要从 ZK 或者 KRaft 的元数据日志里把所有分区状态重建出来。如果此时 ISR 本身是空的重建出来的结果还是 -1但在这十分钟里所有客户端的元数据请求都会超时。更麻烦的是重启期间没法做任何其他修复动作完全在空转。教训是controller 重启应该是最后手段不是第一反应。在确认是控制面问题之前先排除数据面问题。判断方法很简单——如果 broker 日志里能清楚看到磁盘错误、ISR 收缩原因那就是数据面问题重启 controller 一点用没有只有当日志里出现Controller相关的异常、epoch跳变、或者多个 broker 对同一个分区状态描述不一致时才考虑动 controller。6.2 开关开了没恢复埋了半年的雷这个坑值得单独列一条。有一次为了抢救一个日志类 topic临时开了unclean.leader.election.enabletrue救完之后忙着处理别的事忘了关。半年之后另一个核心业务 topic 出了问题ISR 清空集群按那个还留着的开关直接做了一个 unclean 选举选了个落后几万条消息的副本当 leader业务侧丢了数据事后追查了半天才发现是这个老配置。从那以后我养成了一个习惯任何临时性的、降低安全边界的配置变更都要当场写一条带时间的回滚提醒。不依赖记忆也不依赖工单系统。同时如果是 topic 级配置用下面的命令能一次性列出所有被改过的 topickafka-configs.sh --bootstrap-server broker1:9092 \ --describe --entity-type topics --entity-name order-event输出里会显示所有Overridden config。日常巡检时扫一遍有没有不该开的开关被打开。另外强烈建议把unclean.leader.election.enable设成 topic 级而不是 broker 级因为 topic 级的影响面清晰broker 级一旦忘了波及的是整个集群所有 topic。6.3 磁盘写满引发的连锁反应还有一次是典型的“一个分区的问题其实是整机的问题”。收到告警说某个 topic 的几个分区Leader: -1去查的时候发现所有出问题的分区都落在同一台 broker 上而那台 broker 的kafka-broker-api-versions.sh还能连上看起来是活的。顺着查下去磁盘使用率 99.8%。Kafka 对这个情况有个处理逻辑当日志目录无法写入时broker 会把自己标记为不可服务关闭所有分区然后退出。但在退出之前的那一小段时间里它能响应 API 请求却拒绝提供服务于是 controller 认为它在线副本不会立即被判定为离线ISR 却在持续收缩。这个中间态是最难受的看起来一切都正常实际上一片混乱。从这件事上学到的排查Leader: -1时第一个要排除的永远是磁盘。不管症状看起来多么像“网络问题”或者“controller 问题”先跑一遍df -h成本极低。另外Kafka 的日志清理是定期执行的如果清理周期内的写入量超过了清理量磁盘会缓慢爬升等到 100% 才告警就已经晚了。建议在 80% 就设预警留出处理时间。再说一个和磁盘相关的细节如果日志目录是多块盘挂载在同一个log.dirs下任何一块盘满了都可能导致整个 broker 出问题。这种情况下broker 重启后还会在日志里报IOException需要先把满的那块盘上的数据挪走或者删掉才能正常启动。强行重启只会反复失败。这一类问题处理多了我现在的默认动作顺序基本固化成先看df -h和dmesg里有没有 OOM 记录再看 broker 是否在线再看 ISR 状态最后才看 controller。绝大多数情况下第一步就能定位到根因。
阅读完成 · 觉得有帮助?