Kafka 生产环境磁盘坏掉了之后的正确处理姿势
The following article is from 石臻臻的杂货铺 Author 彦祖
生产环境的事故,时间就是金钱(请这位同学一会给我结个账)。来,咱们就一起来帮他看一看。
1. 问题描述
当事人的问题描述
问题整理
Broker 1004 上面的一块磁盘坏掉了; 坏的透透的,也没有 RAID。反正就是这块磁盘数据恢复不了了; 因为 1004 坏了,导致副本离线。如果副本刚好是 Leader,则会触发 Leader 重选举; 然后刚好有一些分区中的 ISR 只有1004,这个时候 1004 副本下线,重选举的时候 Leader 选不出来,就变成了 -1。这些分区此时为不可用状态,需要里面恢复; 如果直接停机 1004 更换磁盘重启,那么势必会造成数据全部丢失; 分区都是 3 副本。
“彦祖,应该怎么办才能将损失降到最低啊?”
2. 分析问题
磁盘不是 RAID 不能容错,想恢复数据是不大可能了。这里我们不考虑其他一些方式恢复磁盘。
一般来说,Kafka 的多副本就是用来应对这种情况的,Follower 副本用来备份容错,这里分区都是 3 个副本。既然1004中的副本丢失了,没有关系,还有其他副本的数据。
但是坏就坏在,有一些分区的 Follower 不在 ISR 里面。ISR 表示的是同步副本,跟 Leader 保持较高的同步。如果配合 ack=all 可以达到最高的可靠性。
ISR 里面只有 1004,如果贸然停机 1004,换上新盘再重启会造成什么情况?
会造成数据全部丢失
当 1004 再次重启的时候,它会再次当选为 Leader。那么,其他的 Follower 副本就会去同步 Leader。如果发现自己的数据跟 Leader 不一致,就会截断自己的数据。这个时候 Leader 没有数据,Follower 截断之后那就全没了。
那我让其他副本当选为 Leader 是不是就可以避免上面的问题了?
嗯,没错。为了避免上面的问题,我们只能先让其他的副本当选 Leader 了。
那么,如何选举其他副本为 Leader 呢?
所有的选举策略最基本的逻辑是:副本在线 && 副本在 ISR 内。但是有一种情况例外。当配置了脏选举,或者主动执行脏选举命令的时候,不在 ISR 内也可以当选,所以执行一次脏选举就可以。 手动修改 ISR 里面的数据。
3. 解决问题
解法一
停止 Broker-1004,换上新的磁盘; 执行一次脏选举 kafka-leader-election.sh --UNCLEAN; 执行完毕之后,会从之前的 Follower 副本里面选出一个作为 Leader; 稍等片刻,等 1004 掉出 ISR 列表之后,再重启 1004 这台机器; 1004 重启之后,开始向 Leader 同步数据。因为是新的磁盘,还没有任何数据,会自动重建。
这里有几个点需要大家思考一下:
当上面步骤 3 执行完毕之后,新的Leader选出来了。作为原来就在ISR 列表中的 broker-1004 会掉出 ISR 列表吗?
答案是 “会” ! 这里涉及到 ISR 的伸缩机制。
简单来说就是,每个 Broker 都会有一个 isr-expiration 缩小定时任务,定时去检查是否满足 ISR 缩小的条件。每隔 replica.lag.time.max.ms/2(2.5 版本开始默认 30000)毫秒执行一次。其中一个条件就是,找到当前 Broker 所有在线的 Leader 分区。
回到我们这个问题。当新的 Leader 选举出来之后,启动了定时任务之后,就会发现之前在 ISR 列表内的 1004,已经慢慢脱离 ISR。
有同学表示,是否可以指定某个副本当选 Leader,比如 Follower 副本中我想挑选一个最大 LEO 当选 Leader 是不是可以将损失降到更低?
解法二
Follower ,副本中我想挑选一个最大 LEO 当选 Leader 是不是可以将损失降到更低?
首先,能用这种方案。那么前提肯定 acks!=all。
因为 ack==all 的情况已经确保了 isr 列表里面的的数据都是一致的。
但是话又说回来,你既然 acks!=all 也就相当于你已经允许一定量的数据发生丢失。所以丢多一点和丢少一点很重要吗(极小的差别)。
官方 Leader 选举策略都是按照 AR 的顺序来选择,是因为它需要保证 Leader 的均衡(为何是保证的 leader 均衡请看分区副本分配策略), 这才是首要的。想要确保数据不丢失请设置 acks=all。
另外,判断 Follower 的 LEO 大小是需要在源码层级调用才知道。那么就需要改源码了。
你想通过 recovery-point-offset-checkpoint 来判断 LEO 的大小是不准确的。这个是记录的分区写入磁盘的 offset,每个 Broker 写入时机你也不清楚(操作系统控制什么时候将 PageCache 写入磁盘),所以不能保证 100% 的准确性。
如果你说一定要这样做(可以但没有必要),那我也给你提供这么一个思路(非源码层面的)。
停止 Broker-1004,换上新的磁盘; 从 Broker 中找到你想要这么做的分区,查看 recovery-point-offset-checkpoint 文件比较一下,找到最大 LEO 的副本。这里你可能会有很多个分区; 修改 zk 中 /brokers/topics/{Topic名称}/partitions/{分区号}/state 节点:把 ISR 改成只剩下你想要设为 leader 的副本 id; 因为 Controller 平时不会监听 state 节点,所以你还需要再创建一个 /isr_change_notification/isr_change_序号 节点。触发 Controller 的监听,让它能触发 Controller 更新内存,还有给 brokers 发送 UpdateMetadata 请求。例如: /isr_change_notification/isr_change_0000000001。
// 这个表示的是 Topic2的0号分区 有ISR的变更
{"version":1,"partitions":[{"topic":"Topic2","partition":0}]}
改了 ISR 之后,仍旧不会触发 Leader 选举。所以,这个时候我们可以手动触发一下 kafka-leader-election.sh --UNCLEAN,仍旧是脏选举哦。PREFERRED 只会选择 AR 的第一个为 Leader; 稍等片刻。等 1004 掉出 ISR 列表之后,再重启 1004 这台机器; 1004 重启之后,开始向 Leader 同步数据。因为是新的磁盘还没有任何数据,因此会自动重建。
上面有关怎么修改节点,节点格式应该是什么样子的,具体详情可以去了解 ISR 伸缩机制。
最后再说一句,除非你非常清楚自己在做什么,否则不要这么干。吃力不讨好的事情。
- EOF -
看完本文有收获?请转发分享给更多人
关注「ImportNew」,提升Java技能
点赞和在看就是最大的支持❤️