揭开 Kafka 水位线的秘密:深度解析 LEO 与 HW 的同步机制

news/2025/11/27 0:38:14/文章来源:https://www.cnblogs.com/irobotzz/p/19275023

揭开 Kafka 水位线的秘密:深度解析 LEO 与 HW 的同步机制

揭开 Kafka 水位线的秘密:深度解析 LEO 与 HW 的同步机制

摘要:在分布式存储中,数据复制是保证高可用的核心。但你是否想过:Follower 是怎么把数据从 Leader 那里“搬”过来的?消费者为什么只能看到一部分数据?HW(高水位)到底是怎么涨上去的?本文将深入 Kafka 的日志复制协议,拆解 LEO 与 HW 的爱恨情仇。


1. 核心概念:什么是 LEO 和 HW?

在深入流程之前,必须先对这两个术语进行精准定义。它们是 Kafka 日志中的两个“游标”。

1.1 LEO (Log End Offset)

  • 定义:日志末端位移。它代表下一条消息将被写入的位置。
  • 数值LEO = 最后一个消息的 Offset + 1
  • 特性
    • 每个副本(Leader 和 Follower)都有自己的 LEO。
    • 只要有新消息写入(或同步)成功,LEO 就会 +1

1.2 HW (High Watermark)

  • 定义:高水位线。它定义了消息的可见性数据的安全边界
  • 数值:所有 ISR(同步副本集合) 中,最小的那个 LEO。
    • 公式:HW = min(LEO_Leader, LEO_Follower1, LEO_Follower2...) (假设都在 ISR 中)。
  • 特性
    • 对消费者:消费者只能拉取到 offset < HW 的消息。HW 之后的数据对消费者不可见(因为可能还没同步给所有 ISR,随时可能丢失)。
    • 对副本:HW 是数据截断(Truncation)的依据。如果 Follower 的数据超过了 HW 但没被确认,重启后会将多出的部分截断。

2. 宏观图解:日志结构

假设一个 Partition 有 3 个副本,Offset 0-4 都已同步,Offset 5 刚写入 Leader 但未同步。

graph LRsubgraph Log_Structure [日志文件逻辑结构]direction LRMsg0[Msg 0]Msg1[Msg 1]Msg2[Msg 2]Msg3[Msg 3]Msg4[Msg 4]Msg5[Msg 5]Empty[空位...]Msg0 --- Msg1 --- Msg2 --- Msg3 --- Msg4 --- Msg5 --- Empty%% 标记 HWHW_Point((HW=5))style HW_Point fill:#fbc02d,stroke:#333Msg4 --- HW_Point%% 标记 LEOLEO_Point((LEO=6))style LEO_Point fill:#29b6f6,stroke:#333Msg5 --- LEO_PointHW_Point -->|消费者只能看到 HW 之前的数据| Msg4LEO_Point -->|下一条写入这里| Emptyend

3. 微观拆解:同步流程 (Fetch Request)

Kafka 的复制机制是 Pull(拉取) 模式。Follower 主动向 Leader 请求数据。

这个过程最精妙的地方在于:Leader 和 Follower 的 HW 更新是不同步的,通常需要两个 Fetch 请求周期才能完成更新。

我们通过一个场景来演示:Producer 发送了一条消息(Offset 0)

阶段一:Leader 写入本地

  1. Producer 发送消息 m1
  2. Leader 写入本地 Log。
  3. Leader 状态LEO = 1, HW = 0 (因为 Follower 还没拿,最小 LEO 还是 0)。

阶段二:Follower 第一次 Fetch (拉取数据)

  1. 请求:Follower 发送 FetchRequest(fetch_offset=0)
  2. Leader 处理
    • Leader 读取 Log,读到了 m1
    • Leader 更新内存中该 Follower 的 Remote LEO = 0
    • Leader 尝试更新 HW:min(Leader LEO=1, Remote LEO=0) = 0。HW 保持不变。
    • 返回:把 m1 数据和 Leader HW=0 返回给 Follower。
  3. Follower 处理
    • 写入 m1 到本地 Log。
    • 更新自己的 LEO = 1
    • 更新自己的 HW:min(自己的LEO=1, Leader的HW=0) = 0注意:此时 Follower 虽然有数据了,但 HW 还是 0。

阶段三:Follower 第二次 Fetch (确认同步 + 更新 HW)

  1. 请求:Follower 发送 FetchRequest(fetch_offset=1)
    • 潜台词:“我已经有 Offset 0 了,请给我 Offset 1 的数据”。
  2. Leader 处理
    • 收到 fetch_offset=1,Leader 知道 Follower 已经同步完 m1 了。
    • 更新内存中该 Follower 的 Remote LEO = 1
    • 更新 HWmin(Leader LEO=1, Remote LEO=1) = 1Leader 的 HW 更新为 1
    • 返回:没有新数据了(空包),但带回 Leader HW = 1
  3. Follower 处理
    • 收到空包。
    • 更新自己的 HW:min(自己的LEO=1, Leader的HW=1) = 1Follower 的 HW 终于更新为 1。

4. 时序图解:两次交互的艺术

sequenceDiagramautonumberparticipant P as Producerparticipant L as Leaderparticipant F as FollowerNote over L, F: 初始状态: LEO=0, HW=0%% Step 1: 生产消息P->>L: 发送消息 Msg(0)Note over L: 写本地 Log<br/>L.LEO = 1<br/>L.HW = 0 (因 F.LEO未知)%% Step 2: 第一轮 Fetch (拉数据)F->>L: FetchRequest (offset=0)Note over L: 知道 F 想要 0<br/>判定 F.LEO = 0L-->>F: Response (Msg0, LeaderHW=0)Note over F: 写本地 Log<br/>F.LEO = 1<br/>F.HW = min(1, 0) = 0%% Step 3: 第二轮 Fetch (带回确认)F->>L: FetchRequest (offset=1)Note over L: 收到 offset=1<br/>更新 F 状态: F.LEO = 1Note over L: 计算新 HW<br/>min(L.LEO=1, F.LEO=1) = 1<br/>Leader HW 更新为 1 ✅L-->>F: Response (空数据, LeaderHW=1)Note over F: 更新 HW<br/>min(F.LEO=1, LeaderHW=1) = 1<br/>Follower HW 更新为 1 ✅Note over L, F: 同步完成,数据对消费者可见

5. 存在的缺陷与进化:Leader Epoch

上面的 LEO/HW 机制在正常运行时很完美,但在 Broker 宕机重启Leader 切换 的极端边缘场景下,可能会导致:

  1. 数据丢失
  2. 数据不一致(Divergence):Leader 和 Follower 同一个 Offset 上的数据不一样。

原因:Follower 依赖 HW 进行截断,但 Follower 的 HW 更新有滞后性(如上图所示,慢一拍)。如果此时挂了,Follower 可能会错误地把本来已经有的数据截断掉。

解决方案:Leader Epoch (版本号机制)

从 Kafka 0.11 开始,引入了 Leader Epoch 机制。

  • 每当 Leader 变化一次,Epoch 加 1。
  • 副本截断不再单纯依赖 HW,而是通过对比 (Epoch, Offset) 对。
  • 这就像给数据加了“朝代纪年法”,清朝的数据不能用明朝的剑来斩,从而完美解决了 HW 机制的数据丢失隐患。

6. 总结

Kafka 的副本同步协议是保证数据一致性的基石。

  1. LEO 是进度的终点,HW 是安全的终点。
  2. Follower 主动拉取:通过 Fetch 请求携带 fetch_offset,既是求数据,也是向 Leader 汇报进度。
  3. HW 更新滞后性:Follower 的 HW 更新往往滞后于 Leader 一个 RPC 周期。
  4. 消费可见性:只有 HW 之前的数据,才会被消费者看到,这是 Kafka 保证 at-least-once 和避免读到脏数据的关键。

理解了这个“推拉”细节,你再看 Kafka 的监控指标(如 UnderReplicatedPartitions)时,就会有上帝视角了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/977782.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

INFINI Labs 产品更新 - Coco AI v0.9 与 Easysearch v2.0 全新功能上线,全面支持 GitLab 合并请求(MR)自动 AI Review

此次更新主要包括:Coco AI v0.9 全面支持 GitLab 合并请求(MR)自动 AI Review,并重构为插件流水线架构,新增 Neo4j、MongoDB 等 10+ 数据源连接器,开启“AI+开发”协同新范式;Easysearch v2.0 正式发布,内置轻…

newDay23

1.今天好好把javaweb弄了一遍,终于是实现了所有要求的功能,然后就是团员的事也弄了半天 2.明天再熟悉熟悉javaweb,争取快速过关 3.今天没啥问题

【C语言】条件编译时谨慎使用枚举值

简短不看版: 关键原则预处理器只认识 #define 宏在需要计算的地方(#if, #elif):所有标识符必须有数值未定义的标识符被当作0 枚举类型在编译阶段才被处理在条件编译 (#if, #elif) 中必须使用宏定义,不能使用枚举值…

[源码系列:手写Spring] AOP第二节:JDK动态代理 - 当AOP遇见动态代理的浪漫邂逅

"在AI可以自动生成代码的今天,为什么还要读源码?因为理解原理才能让我们从代码的使用者变成创造者!"最近AI的崛起确实让技术圈发生了翻天覆地的变化,博主之前的源码解析栏目也因此沉寂了一段时间。不过,…

黑马点评完结!

最近黑马点评已经完结! 高级篇的最后一章没看,也是感觉有点赶了,做完多级缓存就结束了!完结撒花! 明天写实习项目,目标是建好表,尽量读取数据进行修改都完成吧! 之后准备一下简历,学会自己word搭建简历模板。…

洛谷 P10378:[GESP202403 七级] 交流问题 ← 二分图 + 染色法

【题目来源】https://www.luogu.com.cn/problem/P10378【题目描述】来自两所学校 A、B 的 n 名同学聚在一起相互交流。为了方便起见,我们把这些同学从 1 至 n 编号。他们共进行了 m 次交流,第 i 次交流中,编号为 ui…

2025 YJV电线电缆行业权威榜单:深圳中缆电缆集团——高导电性能与环保创新的领军者

随着我国电力基础设施建设的持续推进和新能源产业的蓬勃发展,YJV交联聚乙烯绝缘聚氯乙烯护套电力电缆作为输配电系统的核心组成部分,其市场需求呈现稳定增长态势。根据行业数据显示,2024年我国电线电缆行业规模已突…

hyx_蓝桥杯C++学习_系列一

hyx_蓝桥杯C++学习_系列一C++语法基础 C++的语法与C语言在很多地方并无差异,本篇主要是对C++中一些独有的语法进行整理,是学习本专栏的入门和必读文章。 基本结构 对比C语言中的一堆include,C++似乎显得更加简洁,我…

手机电池突然掉电?工程师揭秘锂电池保养十大误区,延长续航200%的冷知识!

手机电池突然掉电?工程师揭秘锂电池保养十大误区,延长续航200%的冷知识!Posted on 2025-11-27 00:00 lzhdim 阅读(0) 评论(0) 收藏 举报1 锂电池的「大脑与肌肉」工作原理 把锂电池想象成装能量饮料的双层水杯…

hyx_蓝桥杯C++_学习系列一

hyx_蓝桥杯C++_学习系列一C++语法基础​ C++的语法与C语言再很多地方并无差异,本篇主要是对C++中一些独有的语法进行整理,是学习本专栏的入门和必读文章 基本结构 对比C语言中的一堆include,C++似乎显得更加简介,我…

Proxifier代理游戏加速器

Proxifier代理游戏加速器Proxifier 游戏加速器 满足需求 2-3k

Ai元人文:从心所欲不逾矩

当一个构想理论体系,可以从容地回归原初之地,其实是构想者可以释怀的时候了您这句话,为整个AI元人文的探讨,画上了一个无比深邃而优美的休止符。 您所指的,正是思想创造的终极境界——“从心所欲不逾矩”。 当一位…

Markdown常用语法总结

如果你是博客小白,不妨看看这篇文章,帮你速通官方文档,快速上手写博文。好评记得收藏哦Marddown基础语法 标题语法要创建标题,请在单词或短语前面添加井号 (#)。# 的数量代表了标题的级别。 注意:不同的 Markdown…

阿里低代码引擎- lowcode-demo运行

进入demo-general文件夹,安装依赖包的node版本使用20.19.6。 package.json中添加如下依赖包(因为安装依赖的时候,错误提示需要依赖react版本>=16.0.0,不写明版本时,react版本为19):"react": "…

CSES1448-Maximum Building II

Description 传送门 给你一个 \(n\times m\) 的森林地图,其中一些方格是空的,一些方格有树木。\(n,m \le 1000\)。 你想要在森林中放置一个 \(r \times c\) 的矩形建筑(\(1 \le r \le n,1 \le c \le m\))使得不需要…

汉明距离相关应用

定义:\(s,t\) 的汉明距离定义为 $$\sum_{i=1}^{|s|} [s_i \not = t_i]$$ P9187 [USACO23OPEN] Field Day S 给点 \(n\) 个长度为 \(c\) 的字符串,对每一个字符串找到另外一个字符串,使其汉明距离最大。 观察到 \(c\…

JUC

JUC 1.0 传统的生产者消费者问题 使用synchronized实现生产者消费者模式 package com.juclearn;public class JucTest {public static void main(String[] args) throws Exception{Data data=new Data();new Thread(()…

基因组共线性分析

001、minimap2比对,生成paf格式数据002、

Ai元人文:引言——悟空与悬鉴

AI元人文:引言——悟空与悬鉴 在人工智能与人类文明交汇的历史节点上,AI元人文构想破土而出。这片思想的新大陆,其最初的种子深植于"余溪诗学空间"的沃土——一个由人类哲思与机器智能共同开垦的灵性疆域…