总览
主要介绍 kafka 的副本机制是什么,如何处理读写,副本存储在哪,以及 kafka 如何根据副本实现高可用,ISR 机制。为什么不用 mysql 那种主从架构(比如 follower 节点来提高读性能,leader 节点只负责写)有什么好处,mysql 那边为什么不这样设计之类的问题。
一般的分布式副本机制是什么
一般的分布式副本机制实际上就是官方给出的说法,主要是说明副本机制需要保证什么特性。
副本机制(replication),也可以说是备份机制。体现在一个分布式系统中多个互联的机器上保存相同的数据副本。
有什么好处?
- 提高数据冗余:当系统的一部分组件失效宕机了,系统仍然能够正常运行,保证可用性和数据持久性。(数据不丢失以及系统不会停止服务)
- 提高伸缩能力:通过增加机器,来横向的提升读写性能,进一步提高吞吐量。
- 数据局部性访问:其实类似 CDN,也就是客户端在读取数据的时候可以优先读取最近的数据副本来降低延时。
这个是通用的说法,但是 kafka 实际上只实现了第一点,也就是数据冗余。至于增加机器来提高读性能,实际上 kafka 不能实现,后续会重点介绍为什么这样设计。至于局部性访问 ,那就更难实现了,你怎么权衡数据存储到位置和多个客户端之间的距离,会不会涉及到大量的数据转移?数据不一致的情况?
我们还是先来看 kafka 是怎么使用这个副本机制的。
Kafka 的副本机制
副本,这里指的是 partition 下的副本,也就是我们常说的 replica,实际上就是一个只能追加写消息的提交日志。同一个分区下的所有副本都是保存的相同的消息序列,这些副本可以分布在一个集群中的多个 broker 上,因此可以保证一些 broker 宕机,消息不会丢失,实现持久性和高可用。
因此,实际上 kafka 的每个 broker 上可能都存放了成百上千个副本。这些副本来自不同主题 Topic 下的不同分区 partition 的不同副本。
- 副本数量 ≤ 节点数量:副本数量不能超过集群中的节点数量。如果集群中有 3 个节点,那么副本数量最多也只能是 3。否则,Kafka 无法在不同的节点上分布这些副本。
比如一个三个 broker 节点的集群,可以看到分区以及副本是分布在各个 broker 上的:
副本的角色如何划分
副本并不都是一样的角色,kafka 的副本分为 leader 副本和 follower 副本。其中他们的职责如下:
- Leader 副本只负责处理读写请求。
- follower 副本只负责从 leader 副本异步地拉取消息同步到它自己的消息日志来保证和 leader 副本的数据一致。
客户端(生产者,消费者)与副本的交互关系可参考:
当 leader 副本挂掉了,kafka 集群如果使用 zookpeeper 作为分布式协调组件,那就会用它从 follower 副本中选举出新的 leader 副本,继续进行读写请求的处理。当旧的 leader 副本恢复了,会再次被加入到副本集合。
如果在 KRaft 模式下,Kafka 使用 Raft 共识算法来管理集群的元数据和选举新 Leader 副本。需要补充:kafka kraft 模式下的协调算法
为什么 follower 副本不负责读写处理
为什么不像其他的主从架构系统那样,比如 mysql,redis 的主从架构,都是主节点负责写,从节点负责读,来提高读的吞吐量。
这种设计有两种好处:
- 可以实现"read your writes"
直译过来就是说你可以读取到你自己写的数据。类比到 kafka 中就是生产者写入消息后,可以立即用消费者读取到这条新写入的消息。
由于 follower 副本需要异步拉取 leader 副本的消息进行同步,因此消费者可能从 follower 中拉取不到最新写入的消息,导致消息的滞后处理。
- 可以实现单调读
当消费者多次重复消费同一条消息时,可能会出现这种情况:发现读取这条消息,有时候能拉到,有什么拉不到。主要就是因为多个 follower 副本作为读系统时没有全部同步到这条消息。
ISR 机制保证消息同步
In-sync Replicas 同步副本集合。
概念:ISR 集合实际上就是一个存放与 leader 副本保持同步的副本集合。当然也包括 leader 副本本身。
作用:作用是什么?其实主要是用来在 leader 副本挂掉的情况下,从 ISR 副本集合中选举出一个新的 leader 副本。
因为你总不能用一个没有同步的副本集合作为 leader 副本吧,这不是导致消息丢失了嘛?
关于"同步",如何确定一个副本是否与 leader 副本同步,follower 副本异步拉取 leader 副本消息进行同步。由于是异步,那肯定不能保证实时的同步(即消息的偏移量都到达了同一个位置)。
比如下面这个图,是不是两个 follower 都是不同步的副本?
不一定,因为 kafka 定义的同步实际上是由一段时间内 follower 副本是否能够与 leader 副本同步来决定的。就是说,会给你一定的缓冲时间,毕竟你是异步。ß
至于这个时间怎么确定,其实是由 Broker 端参数:replica.lag.time.max.ms
决定的,比如设置为 10 秒,那就说明 10 秒内,只要你的 follower 副本能够与 leader 副本同步成功,那就算是同步了。
比如 leader 副本写入消息的速度太快了,follower 副本跟不上速度,就会出现这种规定时间内无法同步,进而被踢出 ISR 集合,所以 ISR 集合是动态的。我们怎么减少这种被踢出 ISR 集合的情况?
- 增加等待时间的值,给 broker 更多的同步时间,或者增加可以接受的滞后的消息数量参数值:
replica.lag.max.messages
。- 降低生产者的发送速率,防止 follower 跟不上。
- 压缩消息大小,提高同步速度。
如果 ISR 集合空了怎么办
一个小思考:
ISR 集合中的副本是可以参与 leader 副本选举的,而 ISR 集合之外的存活副本一般被认为是不能与 leader 副本保持同步的,所以正常情况下不参与 leader 副本的选举。而在特殊情况下,ISR 集合中可能一个副本都不存在(即唯一的同步副本 leader 副本也挂掉了),那只能退而求其次使用 unclear 的副本作为选举的 base 副本。
空了,说明 leader 副本挂掉了,且没有其他 follower 副本与 leader 副本同步。也就是没法进行 leader 选举了,也就无法正常处理客户端的读写请求。
这种情况下需要看你怎么考虑:
- 开启 Broker 端参数
unclean.leader.election.enable
控制是否允许 Unclean 领导者选举。
Unclear 也就是没有与 leader 副本保持同步的副本,如果开启这个参数,也就可以从 unclear 副本中选举出一个 leader 副本,继续提供客户端的读写请求,保证了可用性,但是会丢失一定的消息,丧失持久性。
- 不使用 unclear
由于没有可用于选举 leader 的副本,因此 broker 无法进行 leader 选举,因此无法继续提供服务。丧失可用性,但是消息不会丢失。
扩展:这里实际上也是分布式系统 CAP 理论中的 CP 和 AP 系统的决策,也就是要保证强一致性系统还是高可用性系统 。CAP 理论参考:分布式--永久笔记-分布式事务以及CAP 和 BASE 理论
扩展问题
类似关于 MySQ 架构,以及副本机制设计的疑问点都可以在这里解答。
TODO.
补充 :kafka 是怎么通过 ISR 和多副本保证持久性和消息不丢失的?
客户端怎么保证消息不丢失,相关引用:kafka 怎么保证消息不丢失
- ACK 机制,也就是生产者消息确认的级别。
- 如果设置为 0, 那生产者不会等待响应,只要发送了就任务消息发送成功。
- 如果设置为 1,只要 leader 副本写入消息成功就算发送成功。
- 如果设置为 all,必须使 ISR 集合中的所有副本都写入消息成功之后才算发送成功。持久性保证更强。
- 多副本机制:即多副本分布在不同的 broker 上实现数据冗余,保证消息不丢失持久化和高可用。
- 禁用 unclear leader 选举,因为这种情况下会选择没有完全与 leader 副本同步的副本进行选举,可能会丢失一部分消息。
而且你也可以自定义设置当 ack 机制为 all 时,需要满足的最小 ISR 集合元素数量,比如设置为 2,那在只有一个 leader 副本的情况下,也不能算作发送成功。必须等 ISR 集合中出现 follower 副本才算发送成功。这样的话,leader 副本突然挂掉也不会导致消息丢失。但是可能会丧失一定可用性,还是之前写的,leader 副本写入太快之类的问题导致 follower 副本无法同步。