Kafka架构篇 - 多副本机制

多副本机制

副本是分布式系统中对数据服务提供的一种冗余方式。为了对外提供可用的服务,往往会对数据服务进行副本处理。

  • 数据副本:在不同的节点持久化同一份数据,当某个节点存储的数据丢失时,可以从副本中读取数据,这是分布式系统解决数据丢失问题的最有效的手段。
  • 服务副本:多个节点提供相同的服务,每个节点都有能力接收外部的请求并进行相应的处理。

Kafka 从 0.8 版本开始为分区引入了多副本机制,通过增加副本数量提升数据容灾能力。同时,Kafka通过多副本机制实现了故障自动转移,在 Kafka 集群中的某个节点失效的情况下仍然保证服务可用。从生产者发出的一条消息,首先会被写入分区的 Leader 副本,然后需要等待 ISR 集合中的所有 Follower 副本同步完成之后才能被认为已经提交,接着更新分区的 HW,进而消费者可以消费到这条消息。

副本:相对于分区而言的,即副本是特定分区的副本。一个分区包含一个或者多个副本,其中一个为 Leader 副本,其余为 Follower 副本,各个副本位于不同的 broker 节点上。只有 Leader 副本对外提供服务,Follower 副本只负责与 Leader 副本进行数据同步。

AR:分区中的所有副本的统称。

ISR:所有与 Leader 副本保持同步状态的副本集合(Leader 副本也是 ISR 集合的一员)。

LEO:标识每个分区的最后一条消息的下一个位置,分区的每个副本都有自己的 LEO。

HW:ISR 中最小的 LEO,俗称高水位,消费者只能拉取到 HW 之前的消息。

失效副本

处于失效状态(同步失效或者功能失效)的副本会被剥离出 ISR 集合,失效副本对应的分区称为失效分区,即 under-replicated 分区

可以通过如下脚本命令查看失效分区:

bin/kafka-topics.sh --bootstrap-server 10.211.55.6:9092,10.211.55.7:9092,10.211.55.8:9092 --describe --topic thing1 --under-replicated-partitions
  • 功能失效:比如副本下线。

  • 同步失效:当 ISR 集合中的一个 Follower 副本滞后 Leader 副本的时间超过 broker 端参数 - replica.lag.time.max.ms(默认10000)就会判定为同步失效。 比如 Follower 副本的 I/O 开销过大导致 Follower 副本同步速度太慢,在一段时间内都无法追赶上 Leader 副本;频繁的 Full GC 导致进程卡住,在一段时间内没有向 Leader 副本发送同步请求。

ISR的伸缩与扩充

当检测到 ISR 集合中有失效副本时,就会收缩 ISR 集合。

ISR的收缩过程如下图:

1、Kafka启动的时候会开启两个与 ISR 相关的定时任务 - “isr-expiration”、“isr-change-propagation”。

2、“isr-expiration” 任务 每隔 replica.lag.time.max.ms / 2 (默认5000ms)检测每个分区是否需要缩减其 ISR 集合,当检测到 ISR 集合中有失效副本时,就会收缩 ISR 集合。如果某个分区的 ISR 集合发生变更,则将变更后的数据记录到 ZooKeeper 的 /brokers/topics//partition//state 节点中。此外,还会将变更后的记录缓存到 isrChangeSet 中。

3、“isr-change-propagation” 任务 每隔 2500ms 检查 isrChangeSet,如果发现 isrChangeSet 中有 ISR 集合的变更记录,则在 ZooKeeper 的 “/isr_change_notification” 路径下创建一个以 “isr_change” 开头的持久顺序节点,并将 isrChangeSet 中的信息保存到这个节点中。

4、Kafka的控制器为 “/isr_change_notification” 添加一个 Watcher,当这个节点有子节点发生变化时,会触发 Watcher 的处理,通知控制器更新元数据并向它管理的 Kafka Broker 节点发送更新元数据的请求,最后删除 /isr_change_notification 路径下已经处理过的子节点。

Note:为了避免频繁触发 Watcher 的处理影响 Kafka 控制器、其它broker节点、ZooKeeper 的性能,Kafka 添加了限定条件,当检测到分区的 ISR 集合发生变化时,还需要检查以下两个条件:

(1)上一次 ISR 集合发生变化距离现在已经超过5s。

(2)上一次写入 ZooKeeper 的时间距离现在已经超过60s。

满足以上两个条件之一才可以将 ISR 集合的变化写入 ZooKeeper 中。

当 Follower 副本不断与 Leader 副本进行数据同步,并最终追赶上 Leader 副本(当前 Follower 副本的 LEO 大于等于 Leader 副本的 HW)时,Follower 副本就有资格进入 ISR 集合。ISR 集合的扩充过程与收缩过程相似,这里不再展开分析。

副本更新LEO、HW的过程

Kafka架构篇 - 多副本机制_第1张图片

生产者往 Leader 副本写入消息,消息被追加到 Leader 副本的本地日志,并且会更新 LEO。

Kafka架构篇 - 多副本机制_第2张图片

之后 Follower 副本向 Leader 副本拉取消息,并且带有自身的 LEO 信息。

Kafka架构篇 - 多副本机制_第3张图片

Leader 副本读取本地日志,返回给 Follower 副本对应的消息,并且带有自身的 HW 信息。

Follower 副本收到 Leader 副本返回的消息,会将消息追加到本地日志中,并且更新 LEO、HW。

Follower 副本更新 HW 的算法:比较当前 LEO 与 Leader 副本返回的 HW 的值,取较小值作为自己的 HW

Kafka架构篇 - 多副本机制_第4张图片

Follower 副本再次请求拉取 Leader 副本中的消息,并且带有更新后的 HW 的值。

Leader 副本收到 Follower 的请求后,选择最小的 LEO 作为 HW。

Kafka架构篇 - 多副本机制_第5张图片

Follower 副本收到 Leader 副本返回的消息,会接着将消息追加到本地日志中,并且更新 LEO、HW。

Leader Epoch 的引入

在 0.11.0.0 版本之前,Kafka 使用的是基于 HW 的同步方式。这种方式可能会出现 数据丢失 或者 Leader 副本和 Follower 副本数据不一致 的问题。

Kafka架构篇 - 多副本机制_第6张图片

Follower 副本在更新 LEO 为 2 和 更新 HW 为 2 之间存在一轮的 FetchRequest/FetchResponse。如果在这个过程中,Follower 副本宕机了,那么重启后会根据之前记录的 HW(读取 replication-offset-checkpoint 文件)进行日志截断,那么会导致 b 这条消息被删除。然后 Follower 副本再向 Leader 副本拉取消息,如果此时 Leader 副本宕机并且 Follower 副本被选举为新的 Leader,接着原来的 Leader 副本恢复后就会成为 Follower 副本,由于 Follower 副本的 HW 不能比 Leader 副本的 HW 高,所以还会进行一次日志截断,由此 b 这条消息就丢失了。或者原来的 Leader 副本无法恢复,b 这条消息也是会丢失的。

Kafka架构篇 - 多副本机制_第7张图片

如果 min.insync.replicas=1 的场景下,上述两个副本处于挂掉状态,Replica B 先恢复并成为 Leader 副本,接着写入消息 c 并更新 LEO、HW 为 2。此时 Replica A 也恢复过来了,成为 Follower 副本并且需要根据 HW 截断日志以及向 Leader 副本拉取数据,由于此时 Replica A 的 HW 也是 2,所以可以不做任何调整。如此一来,Replica A 和 Replica B 就会出现数据不一致的问题。

为了解决上述两个问题,Kafka 从0.11.0.0开始引入了 Leader Epoch 的概念,在需要截断数据的时候使用 Leader Epoch 作为参考依据。Leader Epoch 初始值为 0,代表 Leader 的纪元,每次 Leader 变更,该值都会加一。每个副本在本地的 leader-epoch-checkpoint 文件中记录 StartOffset> 信息。

  • 解决数据丢失问题:Follower 副本重启,先发送请求(包含 Leader Epoch 值)给 Leader 副本,如果 Leader 副本收到请求后发现当前的 Leader Epoch 与 Follower 传过来的 Leader Epoch 一致,则返回当前的 LEO;如果不一致,则 Leader 副本会查找 Follower 传过来的 Leader Epoch + 1 对应的 StartOffset 返回给 Follower 副本。Follower 副本根据 Leader 副本返回的 StartOffset 判断是否需要截断日志。

StartOffset:当前 Leader Epoch 下的写入的第一条消息的偏移量

Kafka架构篇 - 多副本机制_第8张图片

  • 解决数据不一致的问题:还是在 min.insync.replicas=1 的场景下,Replica A、Replica B 都处于挂掉的状态,Replica B 先恢复通过选举成为 Leader并更新 LE,然后写入 c 这条消息并更新 LEO、HW。这时 Replica A 恢复过来成为 Follower,向 Replica B 发送请求并携带自身的 LE。Replica B 接收到请求后,比较两者的 LE 发现不一致,然后返回 Replica B 当前 LE 对应的 StartOffset。Replica A 比较 Replica B 返回的 StartOffset 与自己的 LEO ,判断是否需要日志截断。

Kafka架构篇 - 多副本机制_第9张图片

Kafka架构篇 - 多副本机制_第10张图片

你可能感兴趣的:(Kafka,kafka,架构,分布式)