佩琪: 大佬,你昨天给讲了kafka数据高可靠 主从同步的概念和过程;但还留了个尾巴,kafka主从同步两阶段协议里存在的问题还没有讲了
技术大佬 : (幸亏上次到饭点,否则就尬死了,这个佩琪问题真多,没办法还需要继续装下去)
技术大佬 : 在主从同步时,如果只靠HW和LEO,在某些特殊的情况下 ,确实存在两种问题 。一种是 数据会丢失 ;另外一种是数据可能会不一致。
![](https://file.jishuzhan.net/article/1752584912990375938/fd4bcfbe502800e4a5fc07843bf01a26.webp)
佩琪: 什么样的特殊条件下了?
技术大佬 : 准确说是在 min.insync.replicas=1 这种特殊设置下(级leader partition写入消息到本地,就代表了"消息已提交")
佩琪: 那在这种情况下,依靠HW和LEO的主从同步,是如何导致数据丢失的呢?
技术大佬 : 咱们看图说话?(资料来自于 kafka KIP官方文档)
![](https://file.jishuzhan.net/article/1752584912990375938/48012a2727de2cbd0b5f7d30cc07639d.webp)
技术大佬 : 还记得上次讲的《 kafka是如何做到数据的高可靠的》里面数据同步过程吗? 从上面的图可看出
- (A) 在同步的第一阶段,从 (B)拉取m2消息写入到本地文件系统
- (A) 突然宕机了(还来不及发送第二次请求 获取leader HW)
- 此时副本(A)重新启动。它将其日志截断至高水位线 并向领导者 (B) 发出获取请求。
- (B) 然后失败,A 成为新的领导者。消息m2已经永久丢失(无论B是否回来)。
技术大佬 : 从上面的过程 可看到 这种数据的丢失,是由于kafka 在进行重启恢复时,由截断日志的机制和 min.insync.replicas=1的情况下,共同造成的。
佩琪: min.insync.replicas=1的情况下,数据可能会丢失,这我还能理解,毕竟min.insync.replicas=1 表示只要有一个副本把消息写入成功,就表示消息已提交了。比如leader partition 写入消息到文件系统成功,就表示已提交;但如果leader partition 所在的broker都宕机了,那么在为1的条件下,kafka是不能保证leader切换后, 这种情况消息不丢的。
佩琪: 那依靠HW和LEO,是如何导致数据不一致的呢,我觉得这个才是最要命的?
技术大佬 : 确实,如果多个分区之间的数据都不一致了,问题确实较大。咋们还是看图说话?
![](https://file.jishuzhan.net/article/1752584912990375938/67b3ef5912f8396d47e5f93d17685c80.webp)
- 在min.isr=1的情况下,(A)和(B)同时宕机了
- (B) 先恢复,成为了leader,接受消息M3,HW变为了2
- (A)在恢复,然后截取消息日志,发现HW和LEO相同,就不截取了
- 此时在相同的消息位置1上,出现了不同的消息内容
佩琪: min.insync.replicas=1的情况下,确实恼火,有啥解决方法了?
技术大佬 : 有的,kafka 引入了Leader Epoch 的概念来解决这两个问题 。
技术大佬 : Leader Epoch 包含两部分数据。
一个是 epoch: 表示一个单调递增的版本号。每当leader 发生变更(比如重选变更)时,都会增加;
一个是 start offset: 在新 Leader Epoch 中写入日志的第一个偏移量的消息位置。
这部分数据 每个副本集都有,并且还写入了本地文件系统里。
佩琪: 那 Leader Epoch 是如何解决数据不一致的了?
技术大佬 : 咋们还是看图说话?
![](https://file.jishuzhan.net/article/1752584912990375938/36687c085c87e0a869780696b22a41df.webp)
- (A)和(B)在宕机前;根据Leader Epoch 规则。Leader Epoch为0 ,start offset 为0
- 当 (B) 由于宕机重启后,(B) 成为了领导者;此时Leader Epoch为1 ,start offset为1
- (B) 接受了消息M3;
- (A)此时重启进行恢复;变成了follower,它在截断日志前会发送一个LeaderEpoch 的请求(参数里带上本分区的 leader epoch 0)给当前的领导者;
- 当前领导者 (B) 一查 Leader Epoch为0的log End offset (请求的leader epoch+1 的start offset )为1;然后返回给 (A)
- (A) 拿到返回值,发现leader epoch 为0 的 log end offset为1 ,比自己本地的log end offset 2 还小;那就以leader返回的为准吧;把1 以及后面位置的日志都给截断了;这样在lead epoch为0时,(A)和(B)分区的日志数据保持了一致
- 为了追赶数据,folower(A) 再从leader(B)请求同步数据;把m3消息同步到本地中。最终 (A)和(B)保持了数据同步。
总结
在min.insync.replicas=1 + kafka只根据HW截断日志的情况下;主从分区,会存在已提交消息,在leader切换时;会丢失数据的情况;或者数据不一致的情况;
在min.insync.replicas=1的情况下,leader分区切换,导致已提交消息丢失,这个还能理解;谁让你设置的为1了;但是在这种情况下,发生leader 分区切换,导致数据不一致,却是不能允许的。
kafka已在0.11.0.0 版本中,通过引入 Leader Epoch的概念,解决了上述场景中的问题;降低了此种情况下,leader分区切换导致的数据丢失和不一致的问题。但在min.insync.replicas=1的情况下;仍然存在leader 分区切换后,已提交消息会丢失的问题。
如果要提高broker端消息的可靠性,可参考:《kafka 消息"零丢失"》
原创不易,请 点赞,留言,关注,收藏 4暴击^^
参考资料: