Kafka副本机制以及副本同步

副本

通过下面的命令去创建带2个副本的topic

sh kafka-topics.sh --create --zookeeper 192.168.11.156:2181 --replication-factor 3 --partitions 3 --topic secondTopic

查看主题中分区的leader

  • 在zookeeper服务器上执行,get /brokers/topics/secondTopic/partitions/1/state

{“controller_epoch”:12,“leader”:0,“version”:1,“leader_epoch”:0,“isr”:[0,1]}

  • 或通过sh kafka-topics.sh --zookeeper 192.168.13.106:2181 --describe --topic test_partition

副本类型

  • leader副本:响应clients端读写请求的副本
  • follower副本:被动地备份leader副本中的数据,不能响应clients端读写请求。
  • ISR副本:包含了leader副本和所有与leader副本保持同步的follower副本——如何判定是否与leader同步后面会提到每个Kafka副本对象都有两个重要的属性:LEO和HW。注意是所有的副本,而不只是leader副本。

LEO:即日志末端位移(log end offffset),记录了该副本底层日志(log)中下一条消息的位移值。注意是下 一条消息!也就是说,如果LEO=10,那么表示该副本保存了10条消息,位移值范围是[0, 9]。另外, leader LEO和follower LEO的更新是有区别的。
HW:即上面提到的水位值。(木桶原则,取数据最少的分区下标)对于同一个副本对象而言,其HW值不会大于LEO值。消费者只能拉取到offset小于HW的消息。同理,leader副本和follower副本的HW更新是有区别的
从生产者发出的 一 条消息首先会被写入分区的leader 副本,不过还需要等待ISR集合中的所有 follower副本都同步完之后才能被认为已经提交,之后才会更新分区的HW, 进而消费者可以消费到这条消息。

副本协调机制

image.png
follower副本只负责同步数据以及当
leader副本所在的broker挂了以后,会从follower副本中选取新的leader。写请求首先由Leader副本处理,之后follower副本会从leader上拉取写入的消息,这个过程会有一定的延迟,导致follower副本中保存的消息略少于leader副本,但是只要没有超出阈值都可以容忍。但是如果一个follower副本出现异常,比如宕机、网络断开等原因长时间没有同步到消息,那这个时候,leader就会把它踢出去。kafka通过ISR集合来维护一个分区副本信息
ISR(in-Sync replicas , 副本同步队列)
ISR数据保存在Zookeeper的 /brokers/topics//partitions//state
节点中。ISR集合中的副本必须满足

  1. 副本所在节点必须维持着与zookeeper的连接
  2. 副本最后一条消息的offffset与leader副本的最后一条消息的offffset之间的差值不能超过指定的阈值replica.lag.time.max.ms,kafk副本管理器会启动一个副本过期检查的定时任务,这个任务会定期检查当前时间与副本的lastCaughtUpTimeMs的差值是否大于参数replica.lag.time.max.ms 的值,如果大于,则会把这个副本踢出ISR集合

所有Replica宕机异常

  1. 等待ISR中的任一个Replica“活”过来,并且选它作为Leader
  2. 选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader

这就需要在可用性和一致性当中作出一个简单的折衷。

同步原理

Producer在发布消息到某个Partition时,先通过ZooKeeper找到该Partition的Leader get /brokers/topics//partitions/2/state ,然后无论该Topic的Replication Factor为多少(也即该Partition有多少个Replica),Producer只将该消息发送到该Partition的Leader。
Leader会将该消息写入其本地Log。每个Follower都从Leader pull数据。这种方式上,Follower
存储的数据顺序与Leader保持一致。Follower在收到该消息并写入其Log后,向Leader发送ACK。 一旦Leader收到了ISR中的所有Replica的ACK,该消息就被认为已经commit了,Leader将增加 HW(HighWatermark)并且向Producer发送ACK

  1. leader收到消息


lead收到消息后会把消息追加到log文件中,同时更新leader副本中LEO=1。尝试更新HW值,HW=min(LEO,remote LEO…),因为follower还没有fetch,remote LEO为0,最小值为0,故不会更新HW。

  1. follower fetch消息

  • leader端
  1. 读取log中的数据,根据fetch request中携带的fetch offset=0来更新remote LEO=0
  2. 尝试更新HW,HW=min(LEO,remote LEO…),更新失败
  3. 把消息内容和当前分区的HW值发给follower。
  • follower端
  1. 收到消息后,将消息写入log,同时更新LEO=1。

  2. 更新HW,HW=min(LEO,leader HW),更新失败

  3. 第二次fetch请求

  • leader端
  1. 读取log数据,根据fetch request携带的LEO,更新remote LEO=1.
  2. 尝试更新HW,HW=min(LEO,remote LEO…) HW=1 HW更新成功
  3. 把数据和当前分区的HW值返回给follower副本,这个时候如果没有数据,则返回为空
  • follower端
  1. 如果有数据则写本地日志,并且更新LEO
  2. 更新follower的HW值,HW=min(LEO,leader HW)=1

阻塞问题
前面说过,由于leader副本暂时没有数据过来,所以follower的fetch会被阻塞,直到等待超时或者leader接收到新的数据。当leader收到请求以后会唤醒处于阻塞的fetch请求。处理过程基本上和前面说的一致

数据丢失(数据截断问 题)

前提:min.insync.replicas=1 //设定 ISR中 的最小副本数是多少,默认值为1(在server.properties中配 置), 并且acks参数设置为-1(表示需要所有副本确认)时,此参数才生效。
表达的含义是,至少需要多少个副本同步才能表示消息是提交的, 所以,当 min.insync.replicas=1 的时候,一旦消息被写入 leader 端 log 即被认为是“已提交”,而延迟一轮 FETCH RPC 更新 HW 值的设计使得 follower HW 值是异步延迟更新的(follower的hw是在第二次fetch时的response后更新的),倘若在这个过程中 leader 发生变更,那么成为新 leader 的 follower的 HW 值就有可能是过期的,此时就会擦除数据使得LEO=HW,造成数据丢失使得 clients 端认为是成功提交的消息被删除。
解决方案
在 kafka0.11.0.0 版本之后,引入了一个 leader epoch 来解决这个问题,所谓的 leader epoch 实际上是 一对值(epoch,offset),epoch代表leader的版本号,从0开始递增,当 leader 发生过变更,epoch 就+1,而 offset 则是对应这个 epoch 版本的 leader 写入第一条消息的 offset。

比如 (0,0), (1,50) ,表示第一个leader从offset=0开始写消息,一共写了50条。第二个leader版本号是1,从 offset=50开始写,这个信息会持久化在对应的分区的本地磁盘上,文件名是 /tmp/kafka-log/topic/leader-epoch-checkpoint 。

leader broker 中会保存这样一个缓存,并且定期写入到 checkpoint 文件中

当 leader 写 log 时它会尝试更新整个缓存: 如果这个 leader 首次写消息,则会在缓存中增加一个条目;否则就不做更新。而每次副本重新成为 leader 时会查询这部分缓存,获取出对应 leader 版本的 offset。

我们基于同样的情况来分析,follower 宕机并且恢复之后,有两种情况,如果这个时候 leader 副本没有挂,也就是意味着没有发生 leader 选举,那么 follower 恢复之后并不会去截断自己的日志,而是先发送 一个 OffsetsForLeaderEpochRequest 请求给到 leader 副本,leader 副本收到请求之后返回当前的 LEO。

如果 follower 副本的 leaderEpoch 和 leader 副本的 epoch 相同, leader 的 LEO 只可能大于或者等于 follower副本的 LEO 值,所以这个时候不会发生截断。

如果 follower 副本和 leader 副本的 epoch 值不同,那么 leader 副本会查找 follower 副本传过来的 epoch+1 在本地文件中存储的 StartOffset 返回给 follower 副本,也就是新 leader 副本的 LEO。这样也避免了数据丢失的问题。

如果 leader 副本宕机了重新选举新的 leader,那么原本的 follower 副本就会变成 leader,意味着 epoch 从0变成1,使得原本 followe r副本中 LEO 的值的到了保留。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值