查看原文
其他

Kafka 生产环境磁盘坏掉了之后的正确处理姿势

ImportNew 2022-09-23

The following article is from 石臻臻的杂货铺 Author 彦祖

最近,有位朋友找我向我求助。说他生产环境出事故了,帮他解决一下。



生产环境的事故,时间就是金钱(请这位同学一会给我结个账)。来,咱们就一起来帮他看一看。


1. 问题描述


当事人的问题描述


当事人描述

问题整理


  1. Broker 1004 上面的一块磁盘坏掉了;
  2. 坏的透透的,也没有 RAID。反正就是这块磁盘数据恢复不了了;
  3. 因为 1004 坏了,导致副本离线。如果副本刚好是 Leader,则会触发 Leader 重选举;
  4. 然后刚好有一些分区中的 ISR 只有1004,这个时候 1004 副本下线,重选举的时候 Leader 选不出来,就变成了 -1。这些分区此时为不可用状态,需要里面恢复;
  5. 如果直接停机 1004 更换磁盘重启,那么势必会造成数据全部丢失;
  6. 分区都是 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 呢?


  1. 所有的选举策略最基本的逻辑是:副本在线 && 副本在 ISR 内。但是有一种情况例外。当配置了脏选举,或者主动执行脏选举命令的时候,不在 ISR 内也可以当选,所以执行一次脏选举就可以。
  2. 手动修改 ISR 里面的数据。


3. 解决问题


解法一


  1. 停止 Broker-1004,换上新的磁盘;
  2. 执行一次脏选举 kafka-leader-election.sh --UNCLEAN;
  3. 执行完毕之后,会从之前的 Follower 副本里面选出一个作为 Leader;
  4. 稍等片刻,等 1004 掉出 ISR 列表之后,再重启 1004 这台机器;
  5. 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% 的准确性。


如果你说一定要这样做(可以但没有必要),那我也给你提供这么一个思路(非源码层面的)。


  1. 停止 Broker-1004,换上新的磁盘;
  2. 从 Broker 中找到你想要这么做的分区,查看 recovery-point-offset-checkpoint 文件比较一下,找到最大 LEO 的副本。这里你可能会有很多个分区;
  3. 修改 zk 中 /brokers/topics/{Topic名称}/partitions/{分区号}/state 节点:把 ISR 改成只剩下你想要设为 leader 的副本 id;
  4. 因为 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}]}
  1. 改了 ISR 之后,仍旧不会触发 Leader 选举。所以,这个时候我们可以手动触发一下 kafka-leader-election.sh --UNCLEAN,仍旧是脏选举哦。PREFERRED 只会选择 AR 的第一个为 Leader;
  2. 稍等片刻。等 1004 掉出 ISR 列表之后,再重启 1004 这台机器;
  3. 1004 重启之后,开始向 Leader 同步数据。因为是新的磁盘还没有任何数据,因此会自动重建。


上面有关怎么修改节点,节点格式应该是什么样子的,具体详情可以去了解 ISR 伸缩机制。


最后再说一句,除非你非常清楚自己在做什么,否则不要这么干。吃力不讨好的事情。


- EOF -

推荐阅读  点击标题可跳转

1、被坑惨了!盘点Kafka一些非比寻常的坑

2、13 张图让你学会 Kafka 分区副本同步限流机制

3、深度剖析:Kafka 请求是如何处理? 看完这篇文章彻底懂了!


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

点赞和在看就是最大的支持❤️






您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存