一、HW和LEO
![](https://github.com/AlgoricLi/MyImage/blob/master/BigData/Kafka/HW%E5%92%8CLEO.png?raw=true)
- HW:High Watermark,高水位,在分区高水位以下的消息被认为是已提交消息,反之就是未提交消息,注意高水位本身属于未提交消息,图中的8就属于未提交消息,高水位上的消息是不能被消费者消费的。
- LEO: Log End Offset,日志末端位移,表示副本写入下一条消息的位移值,数字 15 所在的方框是虚线,这就说明,这个副本当前只有 15 条消息,位移值是从 0 到 14,下一条新消息的位移是 15
高水位的作用主要是:
- 定义消息可见性,即用来标识分区下的哪些消息是可以被消费者消费的
- 帮助 Kafka 完成副本同步
高水位和 LEO 是副本对象的两个重要属性。Kafka 所有副本都有对应的高水位和 LEO 值,而不仅仅是 Leader 副本。只不过 Leader 副本比较特殊,Kafka 使用 Leader 副本的高水位来定义所在分区的高水位。换句话说,分区的高水位就是其 Leader 副本的高水位
同一个副本对象,其高水位值不会大于 LEO 值
HW 并不是 Kafka 特有的概念,HW 通常被用在流式处理领域(比如 Apache Flink、Apache Spark 等),以表征元素或事件在时间层面上的进度。在 Kafka 中,对于 Leader 新写入的消息,Leader 会等待该消息被 ISR 中所有的 Replicas 同步后再更新 HW,之后该消息才能被提交从而被 Consumer 消费。
这种机制有一个好处,确保 HW 及其之前的消息(Committed 状态)都是已备份的,即便 Leader 所在的 Broker 因故障下线,那么 Committed 状态的消息仍然可以从新选举出的 Leader 中获取
二、高水位更新机制
Kafka副本之间的数据复制既不是完全的同步复制,也不是单纯的异步复制,这个在前面博客中已经解释过。
![](https://github.com/AlgoricLi/MyImage/blob/master/BigData/Kafka/HW%E5%92%8CLEO%E5%8F%91%E5%B8%83%E5%9B%BE.png?raw=true)
Broker 0 上保存了某分区的 Leader 副本和所有 Follower 副本的 LEO 值,而 Broker 1 上仅仅保存了该分区的某个 Follower 副本
远程副本的作用:帮助 Leader 副本确定其高水位,也就是分区高水位
-
Leader 副本的HW更新原则:取当前leader副本的LEO和所有remote副本的LEO的最小值
-
Follower副本的HW更新原则:取leader副本发送的HW和自身的LEO中的最小值
![](https://github.com/AlgoricLi/MyImage/blob/master/BigData/Kafka/HW%E5%92%8CLEO%E6%9B%B4%E6%96%B0%E6%B5%81%E7%A8%8B%E5%9B%BE.png?raw=true)
上图演示了生产者写入数据到Kafka的leader副本,然后同步到follower副本的流程
副本同步机制流程:
- 生产者写入消息到leader副本
- leader副本LEO值更新
- follower副本尝试拉取消息,发现有消息可以拉取,更新自身LEO
- follower副本继续尝试拉取消息,这时会更新remote副本LEO,同时会更新leader副本的HW
- 完成4步骤后,leader副本会将已更新过的HW发送给所有follower副本
- follower副本接收leader副本HW,更新自身的HW
下面举例详细说明下:
![](https://github.com/AlgoricLi/MyImage/blob/master/BigData/Kafka/HW%E5%92%8CLEO%E8%AF%A6%E7%BB%86%E6%B5%81%E7%A8%8B%E5%9B%BE1.png?raw=true)
初始值都为0
![](https://github.com/AlgoricLi/MyImage/blob/master/BigData/Kafka/HW%E5%92%8CLEO%E6%B5%81%E7%A8%8B%E5%9B%BE2.png?raw=true)
生产者写入1条消息leader的LEO更新为1,follower拉取写入的消息,发现存在消息,更新自身LEO为1
![](https://github.com/AlgoricLi/MyImage/blob/master/BigData/Kafka/HW%E5%92%8CLEO%E6%B5%81%E7%A8%8B%E5%9B%BE3.png?raw=true)
follower继续从leader拉取消息,这会更新remote的LEO为1,比较remote的LEO和leader的LEO,它们都是1,故更新leader的HW为1
完成上一步后,leader会将自身的HW发送给follower,follower会将该值和自身的LEO进行比较,由于这两个值都是1,所以更新follower的HW为1,至此完成副本数据同步
由于Follower 副本的高水位更新需要一轮额外的拉取请求才能实现,Leader 副本高水位更新和 Follower 副本高水位更新在时间上是存在错配的,所以可能存在数据丢失。
在Broker 端参数 min.insync.replicas 设置为 1的情况下,leader的HW已更新,但是为同步到follower,此时leader宕机,会选举follower副本为leader,但是刚刚的数据没有同步过来,且生产者会认为数据写入成功,也不会重发,leader就算恢复也会执行日志截断,数据就丢失了。Leader Epoch 机制来规避这种数据丢失。
三、日志截断
在leader宕机后,只能从ISR列表中选取新的leader,而ISR中的follower副本HW之前的数据都是已提交的
选出了新的leader,而新的leader并不能保证已经完全同步了之前leader的所有数据,只能保证HW之前的数据是同步过的,此时所有的follower都要将数据截断到HW的位置,再和新的leader同步数据,来保证数据一致。
当宕机的leader恢复,发现新的leader中的数据和自己持有的数据不一致,此时宕机的leader会将自己的数据截断到宕机之前的HW位置,然后同步新leader的数据。宕机的leader活过来也像follower一样同步数据,来保证数据的一致性