Kafka如何保证数据不丢失
作者:程序员马丁
在线博客:https://open8gu.com
大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。
回答话术
Kafka 为分区引入了多副本(Replica)机制,通过增加副本数量提升容灾能力,从而做到高可用。简单的来说,在同一分区中,相同的数据会同时被分步在不同 Broker 中的多份副本保存。
在此基础上,副本又分为 Leader 副本和 Follower 副本,它们会构成一主多从关系。其中,Leader 副本负责读写,而 Follower 副本只负责从 Leader 副本同步消息。当 Leader 副本出现故障时,Kafka 会从 Follower 副本中重新选举新的 Leader 副本对外提供服务。Kafak 通过多副本机制实现了故障转移,当 Kafka 集群中的某个 Broker 失效时仍然能保证服务可用。
不过由于副本之间同步有延迟,因此在同一时刻副本间的数据是不能保证完全一致的,为此,Producter 端提供了可选的 ACK 策略,我们可以自己决定是否要等待所有副本都同步完数据才认定消息发送成功。此外,并且为了避免 Consumer 端消费到没有同步到所有副本的消息,Kafka 还引入了 HW(高水位)以及 Leader Epoch 等机制。
问题详解
1. Kafka 的多副本机制
与我们熟悉的 MySQL 或者 Redis 等中间件一样,Kafka 同样采用了增加副本的方式来保证高可用。
首先,我们知道 kafka 中每个 Topic 都分为多个 Partition,在默认的集群环境下,同一个 Topic 下的 Partition 会分布到不同的 Broker 上,在这种情况下,如果某个 Broker 挂了,那么它拥有的 Partition 中的所有数据都会丢失。
为了避免这个问题,从 0.8 版本开始,Kafka 引入了多副本(Replication)机制,简单的来说,就是让集群中的多个 Broker 节点同时持有 Partition 的副本。与 Redis 的主从类似,Partition 副本也分为负责读写的 Leader 副本,与只负责同步数据的 Follower 副本,如此一来,即使某一个 Broker 挂了,也只会导致其中的一个副本丢失数据,而其他的副本依然可以正常运行。
不过,既然是主从结构,那么必然也要面对 Leader 节点宕机后重新选举的问题,这一部分会涉及到 Kafka 集群中的控制器以及选举策略相关的内容,这一部分内容我们会在高可用相关的文章里面进一步的介绍。
2. ISR 集合与 OSR 集合
在 Kafka 中,同一个 Partition 分布在不同 Broker 的所有副本构成的集合被称为 AR(Assigned Replicas)。
其中,在指定时间范围内与 Leader 副本进行过数据同步的副本锁构成的集合又称为 ISR(In-Syn-Replicas)。这个“时间范围”即为配置文件中的 replica.lag.time.max.ms
配置,默认为 10 秒。
简单的来说,如果一个 Follower 副本 10 秒内没有试图从 Leader 副本同步过数据(只要发起了请求就行,不一定真要同步到数据),那么就会被踢出 ISR,换而言之,ISR 中的所有副本,都是最近 10 秒从 Leader 副本同步过数据的,它们都是可信赖的同志。
与 ISR 对应的,所有被踢出 ISR 的副本构成的结合被称为 OSR(Out-of-Sync-Replica)。
也就是说,OSR 中的副本的同步进度实际上都是落后 Leader 比较多的,因此不管是选举还是消费,我们都需要优先选择 ISR 中的副本。
注意,在 0.9 版本之前,ISR 的判定标准是落后的消息条数,即
replica.lag.max.message
参数,在生产中这个条数实际上很难定一个合理的值 —— 尤其是在 Kafka 本身通常被用在大数据场景。这个值过小会导致副本被频繁踢出 ISR,而过大又会导致形同虚设。因此从 0.9 开始这个判断条件被换为了时间,也就是replica.lag.time.max.ms
。
3. 生产者如何保证副本数据一致性
首先,生产者在发送消息时,可以通过 request.required.acks
参数来配置 ACK 的策略,它与副本息息相关:
- 1(默认):只要 Leader 副本收到消息即认为发送成功,此时如果 Leader 副本挂了数据就会丢失。
- 0:不需要等待 Leader 副本收到消息就认为成功,基本没人会用它。
- -1:需要等待 ISR 中所有的 Foller 都收到消息以后才认为发送成功,可靠性最高但是效率比较低。
0 基本没什么需要讨论的,因此关注的重点就是 -1 和 1,这两种 ACK 配置,本质上是对 “要高可用还是数据一致性” 的取舍。
3.1. 优先保证可用性
如果我们要保证可用性,那么我们需要:
- 将 ACK 模式设置为 1,即 Leader 副本收到消息就认为发送成功。
- 修改
min.insync.replicas
,将 ISR 副本最小数量为 1,即有 Leader 一个就够了。 - 将
unclean.leader.election.enabled
设置为 true,即万一 ISR 集合中的副本全挂了,允许从 OSR 中选举新的 Leader。
这种情况会保证只要副本可以有一个可以正常工作的 Leader,那么集群就可以正常工作,不过对应的,由于 ACK 模式为 1 并且 ISR 副本在最坏情况下只有 Leader 一个,这意味着只要 Leader 挂了那数据就会直接丢失。
3.2. 优先保证一致性
如果我们要保证一致性,那么就需要牺牲可用性: