> 文章列表 > 【消息队列】Kafka高水位和Leader Epoch原理

【消息队列】Kafka高水位和Leader Epoch原理

【消息队列】Kafka高水位和Leader Epoch原理

什么是高水位

首先高水位也就是HW,而对应的有LEO,其实这都是Kafka副本中针对位移的概念,其目的就是为了保证多副本间数据的一致性。
LEO (Log End Offet):每个副本的最后一个offset,LEO其实就是最新的offset+1。
HW(High Watermark):所有副本中最小的LEO
【消息队列】Kafka高水位和Leader Epoch原理
图中Completed表示已经完成的,In-Flight 正在进行的工作,高水位代表的是消费位移的位置。

高水位作用

主要作用就是如下两个

  • 定义消息的可见性,消息可以被哪些消费者进行消费。
  • 帮助Kafka完成副本同步
    【消息队列】Kafka高水位和Leader Epoch原理
    消费者可以消费哪些消息?
    低于高水位的消息都可以被消费者消息,即已提交消息,但是对于未提交消息不能被消费者消费。(0-7) .需要注意的是等于高水位的位移,也即8这个消息也不能被消费者消费,因为它属于未提交消息的行列。
    LEO是什么?
    图中,写入的消息有15条,位移0到14,15代表的是下一条新消息即将写入的位置,也即就是LEO。所以高水位到LEO之间都属于未提交消息,不能被消费者消费。所以,同一个副本对象,其高水位值不会高于LEO值。
    高水位和LEO是副本重要的两个属性,所有副本都有LEO和高水位,只不过Leader副本的高水位就是分区高水位。

高水位更新机制

我们知道Leader副本和Follower副本都保存的有HW和LEO,但是在Leader副本所在的Broker中还保存的有所有Follower副本的LEO值。为什么需要保存所有Follower副本的LEO,目的是为了确定Leader副本的高水位,其实就是分区的高水位。
在更新Follower副本的HW和LEO的时候,会同步更新Leader副本的HW和LEO,以及更新所有副本的HW,注意这里不会更新所有Follower副本的HW。
【消息队列】Kafka高水位和Leader Epoch原理
更新机制
【消息队列】Kafka高水位和Leader Epoch原理
什么时候判断副本之间同步呢

  • 进入ISR中
  • 远程Follower副本LEO值落后于副本LEO值,但是不超过默认值10S,replica.lag.time.max.ms

Leader副本更新机制
【消息队列】Kafka高水位和Leader Epoch原理
如上图所示,处理生产者请求的逻辑具体

  • 1.生产者发送消息到Broker
  • 2.Broker将消息持久化到磁盘中
  • 3.获取Leader副本存储所有远程副本的LEO值,是一个集合。
  • 4.获取Leader副本的HW。
  • 5.比较Leader副本的HW和所有远程副本的LEO,获取最小值。

而处理follower副本拉取逻辑

  • 1.从磁盘或者页缓存读取消息
  • 2.使用follower副本请求的位移更新其对应远程副本的LEO值。
  • 3.更新分区高水位值,具体如上过程。

Follower副本更新机制
从Leader拉取消息的处理逻辑

  • 1.写入消息到本地磁盘
  • 2.更新副本的LEO值
  • 3.更新高水位值,
    • a. 获取Leader发送的高水位值,currentHW
    • b. 获取步骤2中更新过的LEO值,currentLEO
    • c. 更新高水位为min(currentHW,currentLEO)

副本同步机制解析

首先我们假设在一个分区两个副本机制下。初始值都是0
1.发送者发送一条消息到Broker
【消息队列】Kafka高水位和Leader Epoch原理
2.首先将Leader副本LEO更新为1,其次远程follower副本拉取Leader消息,也将自己更新为LEO=1
【消息队列】Kafka高水位和Leader Epoch原理
3.虽然Leader副本和follower副本也都将各自的LEO=1,但是对于高水位的更新需要在下次follower副本拉取的过程中进行更新。因为上次follwer副本已经拉取了位移为0的消息,当在此拉取位移为1的消息的时候,Leader副本会进行如下操作,会将leader副本的HW更新为1,远程副本更新为1。接着将已经更新过的高水位值发送给follower副本,follwer副本HW=1,整个过程就结束了。
【消息队列】Kafka高水位和Leader Epoch原理

Leader Epoch机制

上述描述了HW和LEO的概念和更新过程,但是仔细的朋友肯定会发现,其实在follower副本更新HW的过程和leader副本更新HW的过程是存在一个间隔期,而这个间隔期可能会出现数据丢失。当然前提是写入消息的配置 Broker 端参数 min.insync.replicas 设置为 1
我们来描述一下这个场景,
1.首先存在两个副本一个副本ALeader,一个副本B。初始设置时,Leader副本的HW=2,副本B HW=1,这是可能存在的,因为副本B的更新需要以来下一次的拉取才可以更新。
2.当副本B宕机后,重启,会从日志文件中拉取最新的消息,因为只存储来一条消息,即位移为0的位置。当时当副本B去从Leader副本拉取消息的时候,这个时间Leader副本宕机了,没有办法,副本B成为了Leader副本。而副本B只存储了一条消息,而副本A重启回来会从副本B拉取消息,原来HW=2,现在只能更新为1,所以导致了丢失了一条消息。
【消息队列】Kafka高水位和Leader Epoch原理
那么是如何解决上述出现的这种follower副本和Leader副本数据丢失的场景呢?
【消息队列】Kafka高水位和Leader Epoch原理
其实是比较简单的,就是在副本B重启回来之后去拉取Leader副本的LEO值,发现LEO=2,不比自己的小,不会对日志进行阶段,因此可以保留消息1.当Leader宕机之后,也会拉取副本B的LEO,发现也不比自己的小,所以不会出现阶段。通过Leader Epoch机制,可以避免消息丢失。

小结

本篇文章,从什么是HW和LEO 描述其基本概念,目的(消息的可见性和副本同步)然后介绍了在Leader副本和Follower副本更新机制过程中对HW和LEO值的更新,但是因为存在Leader副本和Follower副本时间上的间隔期,导致消息丢失,所以Kafka社区引入了Leader Epoch机制来解决。