从Kafka的可靠性设计体验软件设计之美

目录

[1. Kafka可靠性概述](#1. Kafka可靠性概述)

[2. 副本剖析](#2. 副本剖析)

[2.1 什么是副本](#2.1 什么是副本)

[2.2 副本失效场景](#2.2 副本失效场景)

[2.3 数据丢失场景](#2.3 数据丢失场景)

[2.4 解决数据丢失方案](#2.4 解决数据丢失方案)

[3. 日志同步机制](#3. 日志同步机制)

[4. 可靠性分析](#4. 可靠性分析)


1. Kafka可靠性概述

Kafka 中采用了多副本的机制,这是大多数分布式系统中惯用的手法,以此来实现水平扩展、提供容灾能力、提升可用性和可靠性等。

2. 副本剖析

2.1 什么是副本

副本(Replica)是分布式系统中常见的概念之一,指的是分布式系统对数据和服务提供的一种冗余方式。

2.2 副本失效场景

a. follower副本进程卡住,在一段时间内根本没有向leader副本发起同步请求,比如频繁的Full GC。

b. follower副本进程同步过慢,在一段时间内都无法追赶上leader副本,比如I/O开销过大。

c. 如果通过工具增加了副本因子,那么新增加的副本在赶上leader副本之前也都是处于失效状态的。

2.3 数据丢失场景

在某一时刻,B中有2条消息m1和m2,A从B中同步了这两条消息,此时A和B的LEO都为2,同时HW都为1;之后A再向B中发送请求以拉取消息,FetchRequest请求中带上了A的LEO信息,B在收到请求之后更新了自己的HW为2;B中虽然没有更多的消息,但还是在延时一段时间之后返回FetchResponse,并在其中包含了HW信息;最后A根据FetchResponse中的HW信息更新自己的HW为2。

数据丢失场景(part 1)

可以看到整个过程中两者之间的HW同步有一个间隙,在A写入消息m2之后(LEO更新为2)需要再一轮的FetchRequest/FetchResponse才能更新自身的HW为2。如果在这个时候A宕机了,那么在A重启之后会根据之前HW位置(这个值会存入本地的复制点文件replication-offset-checkpoint)进行日志截断,这样便会将m2这条消息删除,此时A只剩下m1这一条消息,之后A再向B发送FetchRequest请求拉取消息。

数据丢失场景(part 2)

此时若B 再宕机,那么 A 就会被选举为新的leader,B 恢复之后会成为follower,由于follower副本HW不能比leader副本的HW高,所以还会做一次日志截断,以此将HW调整为1。这样一来m2这条消息就丢失了(就算B不能恢复,这条消息也同样丢失)。

数据丢失场景(part 3)

对于这种情况,也有一些解决方法,比如等待所有follower副本都更新完自身的HW之后再更新leader副本的HW,这样会增加多一轮的FetchRequest/FetchResponse延迟,自然不够妥当。还有一种方法就是follower副本恢复之后,在收到leader副本的FetchResponse前不要截断follower副本(follower副本恢复之后会做两件事情:截断自身和向leader发送FetchRequest请求),不过这样也避免不了数据不一致的问题。

当前leader副本为A,follower副本为B,A中有2条消息m1和m2,并且HW和LEO都为2,B中有1条消息m1,并且HW和LEO都为1。假设A和B同时"挂掉",然后B第一个恢复过来并成为leader。

数据不一致场景(part 1)

数据不一致场景(part 2)

之后B写入消息m3,并将LEO和HW更新至2(假设所有场景中的min.insync.replicas参数配置为1)。此时A也恢复过来了,根据前面数据丢失场景中的介绍可知它会被赋予follower的角色,并且需要根据HW截断日志及发送FetchRequest至B,不过此时A的HW正好也为2,那么就可以不做任何调整了。

数据不一致场景(part 3)

如此一来A中保留了m2而B中没有,B中新增了m3而A也同步不到,这样A和B就出现了数据不一致的情形。

2.4 解决数据丢失方案

为了解决数据丢失问题,Kafka从0.11.0.0开始引入了leader epoch的概念,在需要截断数据的时候使用leader epoch作为参考依据而不是原本的HW。leader epoch代表leader的纪元信息(epoch),初始值为0。每当leader变更一次,leader epoch的值就会加1,相当于为leader增设了一个版本号。

A在收到2之后发现和目前的LEO相同,也就不需要截断日志了。之后B发生了宕机,A成为新的leader,那么对应的LE=0也变成了LE=1,对应的消息m2此时就得到了保留,之后不管B有没有恢复,后续的消息都可以以LE1为LeaderEpoch陆续追加到A中。

3. 日志同步机制

在Kafka中动态维护着一个ISR集合,处于ISR集合内的节点保持与leader相同的高水位(HW),只有位列其中的副本(unclean.leader.election.enable配置为false)才有资格被选为新的 leader。写入消息时只有等到所有 ISR 集合中的副本都确认收到之后才能被认为已经提交。位于 ISR 中的任何副本节点都有资格成为 leader,选举过程简单、开销低,这也是Kafka选用此模型的重要因素。Kafka中包含大量的分区,leader副本的均衡保障了整体负载的均衡,所以这一因素也极大地影响Kafka的性能指标。

在采用ISR模型和(f+1)个副本数的配置下,一个Kafka分区能够容忍最大f个节点失败,相比于"少数服从多数"的方式所需的节点数大幅减少。

4. 可靠性分析

生产者客户端参数 acks,相比于0和1,acks=-1(客户端还可以配置为all,它的含义与-1一样,以下只以-1来进行陈述)可以最大程度地提高消息的可靠性。

对于acks=1的配置,生产者将消息发送到leader副本,leader副本在成功写入本地日志之后会告知生产者已经成功提交,如图8-24所示。如果此时ISR集合的follower副本还没来得及拉取到leader中新写入的消息,leader就宕机了,那么此次发送的消息就会丢失。

ack=-1的配置,生产者将消息发送到leader副本,leader副本在成功写入本地日志之后还要等待 ISR 中的 follower 副本全部同步完成才能够告知生产者已经成功提交,即使此时leader副本宕机,消息也不会丢失,如果在消息成功写入leader副本之后,并且在被ISR中的所有副本同步之前leader副本宕机了,那么生产者会收到异常以此告知此次发送失败。

相关推荐
xmjd msup19 分钟前
mysql的分区表
数据库·mysql
Lyyaoo.19 分钟前
【JAVA Spring面经】Spring 事务失效情况
java·数据库·spring
MeAT ITEM25 分钟前
MySQL Workbench菜单汉化为中文
android·数据库·mysql
dovens29 分钟前
PostgreSQL 中进行数据导入和导出
大数据·数据库·postgresql
IOT.FIVE.NO.129 分钟前
claude code desktop cowork报错解决和记录Workspace..The isolated Linux environment ...
linux·服务器·数据库
ErizJ34 分钟前
Kafka | 学习笔记
笔记·学习·kafka
Rick199337 分钟前
mysql 慢查询怎么快速定位
android·数据库·mysql
科技小花8 小时前
全球化深水区,数据治理成为企业出海 “核心竞争力”
大数据·数据库·人工智能·数据治理·数据中台·全球化
X56619 小时前
如何在 Laravel 中正确保存嵌套动态表单数据(主服务与子服务)
jvm·数据库·python
虹科网络安全10 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库