kafka 是如何保障副本间数据一致的

本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

先复习一下相关概念:

  1. 副本

    • 副本是相对分区而言,即副本是特定分区的副本
    • 一个分区包含多个副本,副本分布在不同的 broker 中
    • 其中一个为 leader 副本,其余为 follower 副本
    • leader 副本对为提供读写服务,follower 副本只提供同步备份,不对外提供服务
  2. AR/ISR

    • 分区中所有副本统称 AR
    • 其中与 leader 副本保持同步状态的副本集合 → ISR ⇒ [leader, sync followers]
  3. LEO/HW

    • LEO 标示每个分区中下一条消息的写入点 → 每一个分区副本都有自己的LEO
    • ISR 中最小的 LEO ⇒ HW,俗称高水位,消费者只能拉取 HW 之前的代码

producer send msg 会发生什么?

→ send(msg)

→ 写入分区的 leader 副本中,需要等待 ISR 中的 follower 都同步完毕,才能被认为是已经提交,然后更新该分区的 HW

→ 消费者才能开始消费此 msg

LEO 与 HW

首先对于分区副本,最重要的是消息写入/消费。

消费写入 → LEO,每一个分区副本都有一个 LEO;follower 和 leader 同步 LEO 和 ISR 有关系

消费消费 → HW,根据每一个副本 LEO 来确定 leader HW (只有 leader 才有读写服务,所有 leader HW 才有被 consumer 消费的机会)

这里总结一下,leader/follower LEO 以及 HW 更新原则:

不然说着说着,都不知道怎么更新的,讲多了会迷糊~~~

LEO 更新

leader:

  • producer send msg → leader 写入本地磁盘,LEO+1

leader remote:

  • follower fetch request → leader 过程中发生
  • 在 fetch req 中会携带 follower 要从哪个 offset 开始拉取 msg ⇒ pull offset = remote LEO

follower:

  • follower fetch request → leader 过程中发生
  • fetch 到 follower ,紧接着写入本地磁盘,LEO+1

HW 更新

leader:

  • leader 更新 LEO 后
  • 更新完 remote follower LEO 之后 ⇒ min{leader LEO, sync follower remote LEO...}

follower:

  • fetch req → follower 收到 resp {msg, leader HW},更新本地 LEO 之后
  • **follower HW = min{follower LEO, remote leader HW}**

在 HW 更新机制下,需要两轮才可以更新完毕:

  1. follower fetch msg → 获取到新的 msg ,更新本地 msg,LEO+1

  2. follower fetch req → 获取下一条 msg,发送自己当前LEO

    • leader update remote follower LEO ⇒ leader update HW
    • leader send resp{leader HW} ⇒ follower updater HW

这样才更新完两边的 LEO/HW

Epoch 机制

leader epoch 并没有改变 HW 机制的整体运行,改进的只是 leader/follower 重启恢复后的日志截断动作

为什么不支持读写分离

我们想想支持 读写分离 的组件:mysql。

mysql 主从数据在同步过程存在一个数据延延时的时间窗口,在这个窗口主从节点数据不一致。

kafka 如果在进行主从同步,需要:

net I/O → leader mem → leader disk → net I/O → follower mem → follower disk

从网络 → 磁盘 → 网络,整个过程延时大,对与 kafka 这种实时性要求的组件,不太合适。

其次在主从同步里面,最明显的问题就是:read sync msg → 就是我在副本是不能立马读到 msg,这个和前面说的有时间窗口一个意思 ⇒ 方便 read what you write

在副本同步过程中,如果允许 副本读 → 很可能会引起 副本间数据同步延时 :在 A 中以及同步,目前可读;但是在 B 中正在同步 leader,就会出现 msg 时而可读时而不可读。

总结:

  1. 同步过程中,出现数据不一致的时间窗口
  2. 同步延时高
  3. 方便实现 Read-What-You-Write
  4. 方便实现 Monotonic Reads

但是,虽然副本不可读,但是副本本身的职责:同步 leader 数据,这个得保障 → 它们的动作就是不断异步拉取 leader 副本的数据。

既然是异步的,那么在一定时间内,leader 和 follower 的数据肯定是不一致的 (这个也是为什么 follower 不可读的又一个证明) ,那我们应该怎么定义 leader 和 follower 之间同步的标准?

因为只有知道标准,server 才认为 msg 已经在副本间备份完毕,才不会有数据丢失的可能 (当然了,如果 server 存储的磁盘都没有了,数据肯定丢失了。此处 “丢失” 指的是正常情况)

此处就引出:ISR → In-sync Replicas ⇒ ISR 中的副本都是与 Leader 同步的副本

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享