kafka副本机制详解

副本机制(Replication)

又称为备份机制,通常是指在分布式系中在多台机器中存储相同的数据进行备份的机制,副本机制只要有3个好处。

  • 提供数据冗余:即使部分机器出现故障,系统仍然可以提供服务,增加了整体的可用性和数据持久化。
  • 提供高伸缩性:支持横向扩展,可以通过增加副本数,来提供读性能。
  • 改善数据局部性:允许将数据放入与用户地理位置相近的地方,从而降低系统延时。

但是kafka的副本机制只提供了第一个特点,即提供数据冗余的特性。

kafka副本定义

在kafka中一个主题下面可以有多个分区(partition),每个分区(partition)可以有多个副本,所以kafka的副本的维度是以分区为维度进行划分的;同一个分区下的所有副本保存有相同的消息序列,这些副本分散保存在不同的 Broker 上,从而能够对抗部分 Broker 宕机带来的数据不可用。在生产环境中,每个分区的副本分布在不同的机器上,这样即使某些机器出现故障,集群依然可用。
kafka副本机制详解_第1张图片
副本的角色定义:在kafka中副本是有一个leader节点和多个follower节点组成,leader节点负责接收消息和消费消息,follower既不提供写服务也不提供读服务,仅仅用于同步leader副本的消息。follower副本的唯一作用就是当leader副本出现问题时,通过ZooKeeper 提供的监控功能能够实时感知到,并立即开启新一轮的领导者选举,从追随者副本中选一个作为新的领导者。
kafka副本机制详解_第2张图片

kafka这样设计好处:
方便实现读写一致:因为只在leader副本上进行读写操作,所以生产者写入什么消息,消费者就能读到什么消息,消费者不会从follower上进行读取操作,避免了主从同步过程中的延迟问题。
方便实现单调读(Monotonic Reads):在进行多次消费时,不会存在某条消息一会存在,一会消失的情况。如果2 个追随者副本 F1 和 F2,它们异步地拉取领导者副本数据。倘若 F1 拉取了 Leader 的最新消息而 F2 还未及时拉取,那么,此时如果有一个消费者先从 F1 读取消息之后又从 F2 拉取消息。
kafka不像Mysql那样提供读写分离,是因为kakfa本身的设计是通过分区来进行分担集群读写的压力,从架构层面可以很好的进行水平扩展,提供读写分离是在一定程序软件架构设计的不足,才需要进行读写分离,来分担集群的读写压力;更多的是一种弥补方法。

ISR集合

所谓的ISR集合,就是指副本中的消息和leader副本的消息是同步的,没有落后太多的,其中集合中,是包含leader副本自身的。
那么进入或踢出ISR集合是需要满足什么条件呢?这个是通过broker端参数replica.lag.time.max.ms的值来进行设置的,表示follower副本能够落后 Leader 副本的最长时间间隔,默认值为10秒,当一个follower副本的消息落后于leader副本10s钟,就会从ISR集合中进行踢出,当落后的消息少于10s时,又回将这个节点加入到ISR集合中。

如何进行ISR的动态扩缩:Kafka在启动的时候会开启两个任务,一个任务用来定期地检查是否需要缩减或者扩大ISR集合,这个周期是replica.lag.time.max.ms的一半,默认5000ms。当检测到ISR集合中有失效副本时,就会收缩ISR集合,当检查到有Follower的HighWatermark追赶上Leader时,就会扩充ISR。除此之外,当ISR集合发生变更的时候还会将变更后的记录缓存到isrChangeSet中,另外一个任务会周期性地检查这个Set,如果发现这个Set中有ISR集合的变更记录,那么它会在zk中持久化一个节点。然后因为Controllr在这个节点的路径上注册了一个Watcher,所以它就能够感知到ISR的变化,并向它所管理的broker发送更新元数据的请求。最后删除该路径下已经处理过的节点。

ISR集合的作用主要有两个方面

  • leader选举范围:当leader部分挂掉后,某个follower副本会被选为新的leader副本,能够被选为leader副本的条件就是需要在ISR集合中,当然这个是可以进行参数配置的,broker有一个参数unclean.leader.election.enable ,用来进行控制是否可以从非ISR集合中的副本选为leader节点。如果设置为true,开启 Unclean 领导者选举可能会造成数据丢失,但好处是,它使得分区 Leader 副本一直存在,不至于停止对外提供服务,因此提升了高可用性。反之,禁止 Unclean 领导者选举的好处在于维护了数据的一致性,避免了消息丢失,但牺牲了高可用性。这个就是CAP原理中,C和A的取舍问题。
  • 生产ack为-1发送写入的数据::生产者发送消息后,消息需要写入ISR集合中全部副本,才算提交成功;ISR集合中,只有leader一个节点,那么这个时候-1就退化为了1。

副本同步机制

在kafka中,follower副本需要定期从leader部分中拉取消息,在进行消息拉取时,主要有两个概念需要弄清楚

  • 高水位(HW):定义消息可见性,即用来标识分区下的哪些消息是可以被消费者消费的:只有在高水位以下的消息才能被消费者进行消费;并且利用次机制来完成kafka副本的消息同步。
  • 日志末端位移(LEO):即Log End Offset,表示消息下一条消息写入的位置,注意此时这条消息是未写入到kafka中的。

在kafka中每个副本都有自己的高水位和LEO信息。
kafka副本机制详解_第3张图片

高水位更新机制

在kafka中,leader副本所在的broker节点上,会保存所有follower副本的LEO值。在leader副本所在broker节点上的follower副本值,称之为远程副本(Remote Replica),kafka在运行过程中,会不断更新Broker 1 上 Follower 副本的高水位和 LEO 值,同时也会更新 Broker 0 上 Leader 副本的高水位和 LEO 以及所有远程副本的 LEO,但它不会更新远程副本的高水位值。broker0保存远程副本的信息值,就是为了leader副本来确定高水位值;leader副本的HW就是整个分区的HW值。
kafka副本机制详解_第4张图片
每个值的更新时机

  • Broker1上的follower副本会从leader副本拉取消息,写入到本地磁盘后,会更新其LEO的值。
  • Broker0上的leader副本接收到生产者发送的消息,写入到本地磁盘后,会更新其LEO的值。
  • Follower副本从leader副本拉取消失时,会告诉leader副本从哪个位置开始拉取消息。leader副本收到这个消息后,会更新本机上对应的远程副本的LEO值。
  • Follower副本成功更新LEO值后,会比较其LEO值,和Leader副本发过来的HW的值,取两者的最小值来更新自己的HW值。
  • 当Leader副本更新完自身LEO值(或者更新了远程副本的LEO值)时,然后比较Leader副本和所有远程部分的LEO值,其最小值作为自己的HW值,并进行更新。

副本同步流程解析

  • 在初始状态下,所有值都为0
    kafka副本机制详解_第5张图片
  • 当生成者给主题发送消息后,leader分区写入消息后,leader的LEO变为1
    kafka副本机制详解_第6张图片
  • 此时follower过来拉取消息,消息拉取成功,将自身的LEO也更新为1,此时每个副本的HW还是为0,需要在下一次fetchOffset请求时,更新对应的值
    kafka副本机制详解_第7张图片
  • 当follower再次拉取消息时,follwoer副本请求拉取的值为1,Leader 副本接收到此请求后,更新远程副本 LEO 为 1,然后更新 Leader 高水位为 1。做完这些之后,它会将当前已更新过的高水位值 1 发送给 Follower 副本。Follower 副本接收到以后,也将自己的高水位值更新成 1。
    kafka副本机制详解_第8张图片

高水位存在的问题:Leader 副本高水位更新和 Follower 副本高水位更新在时间上是存在错配的。这种错配是很多“数据丢失”或“数据不一致”问题的根源。为了防止,这种问题,社区在0.11版本之后,引入了Leader Epoch 概念,来规避因高水位更新错配导致的各种不一致问题。

数据丢失场景分析:因为整个消息同步过程,存在HW同步的一个延迟,当A的HW为1,B的HW为2值,此时A还没同步到B的HW值时,RelicaA发生宕机,A重启后会根据之前的HW值(保存在本地replication-offset-checkpoint中)进行日志截断,这时消息m2就会丢失,刚好此时ReplicaB发送宕机,ReplicaA被选为了Leader节点;由于follower副本的HW不能比Leader的HW值高,节点B在恢复后也会进行日志截取,消息m2就会彻底丢失掉。
kafka副本机制详解_第9张图片

Leader Epoch

可以理解为Leader的版本,由两部门组成

  • Epoch,一个单调递增的版本号,每当发送leader变更时,就会增加,小版本号的 Leader 被认为是过期 Leader,不能再行使 Leader 权力。
  • 起始位移(Start Offset),Leader 副本在该 Epoch 值上写入的首条消息的位移。

举例说明:假设有两个Leader Epoch,Leader Epoch<0, 0> 和 <1, 120>,第一个Leader Epoch表示版本号为0,Leader 从位移 0 开始保存消息;在写了120条消息后,发生了leader变更,新的leader的版本号为1,起始位移为120。

如何防止数据丢失:此时如果A发生了宕机重启后,并不会马上进行日志截取,而是会向Leader发送一个OffsetsForLeaderEpochRequest请求,那么此时Leader返回的是2,此时该 LEO 值不比它自己的 LEO 值小,而且缓存中也没有保存任何起始位移值 > 2 的 Epoch 条目,因此 A 无需执行任何日志截断操作。
kafka副本机制详解_第10张图片

你可能感兴趣的:(中间件,kafka)