Kafka可靠性保证

多副本机制

Kafka为分区引入了多副本(Replica) 机制, 通过增加副本数量可以提升容灾能力。副本之间是“一主多从”的关系,其中leader副本负责处理读写请求,follower副本只负责与 leader 副本的消息同步。副本处于不同的broker ,当leader副本出现故障时,从follower 副本中重新选举新的 leader 本对外提供服务。
Kafka通过多副本机制实现了故障的自动转移,当集群中某个broker失效时仍然能保证服务可用。
如图是集群中有4个Broker,该主题有3个分区,每个分区有3个副本,即副本因为3,1主2层。

image.png

消费者侧也具备一定的容灾能力,比如前面提高的位移提交信息持久化,可保证消费者宕机,新的消费者依然能从上次提交的消费位置处重新消费,不会造成消息丢失。

几个概念:

  • AR(Assigned Replicas):分区的所有副本。

  • ISR(In-Sync Replicas):所有与leader副本保持一定程度同步的副本(包括 leader 副本在内),这个落后多少是可配置的。只有ISR中的副本才有可能成为leader。

  • OSR(Out-of-Sync Replicas):相对已leader副本同步滞后过多的副本(不包括leader)。

  • HW(High Watermark):高水位,标识特定的偏移量,消费者只能消费到这个偏移量之前的消息。

  • LEO(Log End Offset):标识当前日志文件中下一条待写入消息offset。

HW和LEO的关系如下:

图一中,leader和follower完全同步的情况下,此时HW和LEO都为3,标识当前只能消费到offset=3之前的消息,下一条待写入消息的offset为3。此时,生产者发送两条消息3和4。
图二时刻,新消息首先写入leader副本,follower副本前来请求同步。
图三时刻,follower1同步了消息3和4,而此时follower2只同步了3,于是HW=3,LEO分别为5/5/4
图四时刻,follower2同步完成,整个分区中HW和LEO均为5。

图一
图二
图三
图四

如何判定消息写入成功

通过生产者客户端ack参数控制。

  1. ack=1,默认设置。生产者将消息发送到leader副本,leader副本在成功写入本地日志之后会告知生产者己经成功提交,但是在同步follower过程中宕机,此消息会丢失。
image.png
  1. ack=-1,生产者将消息发送到leader副本,leader副本在成功写入本地日志之后还要等待ISR中的follower副本全部同步完成才能够告知生产者已经成功提交,即使此时leader副本宕机,消息也不会丢失。若follower同步完成之前,leader宕机,生产者会受到异常提示发送失败。
image.png
  1. ack=0,发送消息后,不需要等待服务端响应,默认认为成功,即使leader写入失败,性能最高,可靠性最差。

这种机制既不是完全的同步,也不是完全的异步。这种基于ISR方式的复制是可靠性和性能之间权衡的结果。

对于ack=-1的情况,试想一下这种场景:如果leader消息流入较快,而follower同步速度较慢,最终在某个时间点时所有的follower全部被踢出了ISR列表,即ISR只有一个leader副本,这时候就退化为ack=1的情况,加大了消息丢失的风险。

针对上述问题,kafka提供min.insync.replicas参数指定ISR集合中的最小副本数,如果不满足条件的话,会抛出异常。

min.insync.replicas提升可靠性的同时也会影响到可用性,比如设置为2,不开启该参数的时候,ISR只有一个leader副本时,kafka起码还能运行;但是开启后会导致消息无法写入。

这种方式与少数服从多数方式的区别?

失效副本

正常情况下,分区的所有副本都处于ISR集合中,在某些异常情况下会导致副本被剥离出 ISR集合,这些副本统称为失效副本。

失效的几种情况:

  1. broker宕机

  2. follower副本之后leader副本超过指定之间,默认10s。

    1. 副本进程卡主
    2. 副本进程同步较慢,在一段时间内都无法追上leader副本。

踢出过程:kafka会记录副本上次全部同步的时间(lastCaughtUpTimeMs),只有当副本将leader副本LEO之前的日志全部同步时,才会更新该时间。同时kafka会有定时任务检测当前时间与lastCaughtUpTimeMs的差值,如果超过10s,将副本移除ISR。

重新加入:当副本的LEO > leader的HW时,会重新回到ISR中。

将滞后副本踢出ISR的目的是为了防止leader挂掉后,滞后过多的副本成为leader造成大量数据丢失。

副本间数据同步&一致性保证

整个数据同步的过程如下:

  1. 生产者客户端发送消息至 leader 副本中 。

  2. 消息被迫加到 leader 副本的本地日志,并且会更新日志的偏移量。

  3. follower副本向 leader副本请求同步数据。

  4. leader副本所在的服务器读取本地日志,并更新对应拉取的 follower副本的信息。

  5. leader副本所在的服务器将拉取结果返回给 follower 副本 。

  6. follower副本收到 leader 副本返回的拉取结果,将消息追加到本地日志中,并更新日志的偏移量信息。

image.png

上述流程在leader副本切换的时候可能会导致数据丢失,举例如下:

image.png
image.png

图①时刻,副本A是follower,副本B是leader,B中有两条消息,A从B中同步了消息m1和m2,此时A和B的LEO都是2,HW都为1。

follower请求从leader拉取日志时,会携带自身的LEO,leader会记录各follower的LEO,leader会选取自身LEO和所有follower LEO中的最小值作为leader的HW,同时将HW返回给follower。

follower会选取LEO和leader返回HW中的较小者作为自身的HW。

当A再次请求follower拉取日志时,携带自身LEO=2,此时leader会将HW更新为2,B中没有多余消息,会在等待一段时间后,返回给A,此时A和B的HW都为2。
上面存在一个问题:A在写入消息m2需要再经过一次轮询后,才能将自身HW更新为2。
如图②所示,如果B返回数据前A宕机,A重启后会根据自身HW位置做日志截断,此时m2消息从A中删除,然后A再次向B发起请求时,B宕机(如图3)此时A成为leader,B恢复后成为follower,B也会从HW处截断日志,最终导致m2彻底丢失。

Leader Epoch

Kafka 从 0.11.0.0 开始引入了 leader epoch 的概念,在需要截断数据的时候使用 leader epoch 作为参考依据而不是原本的 HW。

leader epoch 代表 leader 的纪元信息,初始值为 0。每当 leader 变更 一次,leader epoch 的值就会加1,相当于为 leader增设了一个版本号。与此同时,每个副本中还会增设一个矢量 StartOffset>,其中StartOffset表示当前 LeaderEpoch下写入的第一条消息的偏移量。

还是以上面的场景举例,看引入Leader Epoch后是如何解决这个问题。

image.png
image.png

图①时刻,A和B的leader epoch值都为0。A重启后,A会先发送 OffsetsForLeaderEpochRequest(携带A中最新的leader epoch,简称LE_A)给B,如果A和B的LE一样,B会直接返回自己当前的LEO。如果不一样的话,B会查找LE_A+1 的leader epoch对应的StartOffsets作为LEO返回给A,此时A判断出返回值中的LEO与当前LEO相同,所以做数据的截断,因此无数据丢失问题。

image.png

即使在图③时刻B宕机,A成为新的leader,LE_A从0变为1,对应的消息m2得到的保留,不管B有没有恢复,A都可以使用新的leader epoch追加消息

leader副本选举

如果一个分区的 leader 副本不可用,那么就意味着整个分区变得不可用 ,此时就需要 Kafka 从剩余的 follower 副本中挑选一个新的 leader 副本来继续对外提供服务。

该主题的分区及副本会尽可能均匀地分布到 Kafka 集群的各个 broker节点上,对应的 leader副本的分配也比较均匀。对同一个分区而言,Kafka集群的一个 broker 中最多只能有它的一个副本。

通常来讲,如果一个broker上leader副本数过多,那么意味着该broker的负载会比较高,会影响整体的健壮性和稳定性。

如何,一个分区数和副本数都为3的topic初始分区分配结果如下:

image.png

某一时刻,分区1 leader宕机,分区1 leader节点由2变为0。当原来的leader恢复后,他只能作为follower重新加入集群,最终还是导致节点0负载最高,出现负载失衡。

image.png

优先副本

为了能够有效地治理负载失衡的情况 ,Kafka引入了优先副本( preferred replica) 的概念 。所谓的优先副本是指在AR集合列表中的第一个副本 。比如上面主题 topic-partitions中分区1的AR集合列表为[1,2,0], 那么分区0的优先副本即为1。理想情况下,优先副本就是该分区的 leader副本,所以也可以称之为 preferredleader。 Kafka要确保所有主题的优先副本在Kafka集群中均匀分布,这样就保证了所有分区的leader均衡分布。

优先副本选举

通过一定的方式促使优先副本选举为leader副本,以此来促进集群的负载均衡, 这一行为也可以称为“分区平衡” 。
在Kafka中可以提供分区自动平衡的功能,可通过auto.leader.rebalance.enable,默认值为 true。如果开启分区自动平衡的功能,则 Kafka的控制器会启动一个定时任务,这个定时任务会计算每个broker节点的分区不平衡率(非优先副本的leader个数/分区总数)是否超过参数配置的比值,默认值为 10%,如果超过设定的比值则会自动执行优先副本的选举动作以求分区平衡。执行周期默认5分钟 。
不过在生产环境中不建议开启自动平衡, 因为这可能引起负面的性能问题,也有可能引起客户端一定时间的阻塞 。因为执行的时间无法自主掌控 ,如果在关键时期(比如电商大促波峰期)执行关键任务的关卡上执行优先副本的自动选举操作,势必会有业务阻塞、频繁超时之类的风险

可靠性分析

综上所述,我们来看下kafka是如何保证高可靠性。

  1. 多副本机制,副本数越多可靠性越强,但过多副本也会带来性能的下降,一般而言副本数为3能满足大多数场景下多可靠性的要求,更高的场景可以设置为5。

  2. 消息生产过程的ack机制。

  3. 还有个参数unclean.leader.election.enable可控制让非ISR列表中的副本成为leader,这样会造成数据丢失,影响了可用性,但起码还有存活的副本。

  4. 生产重试机制。可设置重试次数和两次重试之间的间隔,通过适当调大重试间隔来提高生产成功率。

  5. 消费端的位移提交控制。

    1. 自动提交位移会导致数据丢失和重复消费的问题。

    2. 手动提交位移不会导致数据丢失。

  6. 消费回溯。对漏掉的消息进行回读,进一步提高了可靠性。

你可能感兴趣的:(Kafka可靠性保证)