所谓的副本机制(Replication),也可以称之为备份机制,通常是指分布式系统在多台互联网的机器上保存相同的数据拷贝。副本机制有什么好处么?
- 提供数据冗余:即使系统部分组件失效,系统依然能够继续运转,因而增加了整体可用性以及数据持久性
- 提供高伸缩性:支持横向扩展,能够通过添加机器的方式来提升读的性能,进而提高读操作吞吐量
- 改善数据局部性:允许将数据放入与用户地理位置相近的地方,从而降低系统延时
这些有优点都是在分布式系统教科书中最长被提及的,但是有些遗憾的是在kafka中目前只能享受到副本机制带来的第1个好处,也就是提供数据冗余备份。
副本定义
kafka是有主题概念的,而每一个主题又进一步划分成若干个分区。副本的概念实际上是在分区层级下定义的,每个分区配置有多若干个副本。
所谓的副本,本质上就是一个只能追加写消息的提交日志,根据kafka副本机制的定义,同一个分区下的所有副本保存有相同的消息序列,这些副本分散的保存在不同的Broker上,从而能够对抗部分Broker宕机带来的数据不可用。

副本角色:
既然分区下面有很多副本,而且这些副本的内容换还要保持一致,消息同步最常见的解决方案就是采用基于领导者(Leader-based)的副本机制。kafka就是这样设计的。

第一,在kafka中,副本分成俩类:领导者副本和追随者副本。每个分区都要在创建的时候都要选举一个副本,称为领导者副本,其余的副本自动称为追随者副本。
第二,在kafka中,追随者副本是不对外提供服务的。这就是说,任何一个追随者副本都不能响应消费者和生产者的读写请求。所有的请求都必须由领导者来处理。最随着位移的任务就是从领导者副本异步拉取消息,并且写入到自己的提交日志中,从而实现与领导者副本的同步。
第三,当领导者副本挂掉了,kafka以根据zookeeper在追随者副本中重新选举一个领导者副本出来,老的领导者副本恢复之后称为新的追随着。
kafka追随者副本不对外提供服务,那么就不能实现副本机制的另外俩个优势,这是为什么呢?其实有下面俩个好处:
1.方便实现“Read-your-write”
所谓的“Read-you-write”,顾名思义就是,当你使用生产者API向kafka成功写入消息之后,马上使用消费者API去读取刚才的消息。当你写完一条消息之后,你肯定希望立即就能看到他,典型的场景就是微博和微信朋友圈。如果允许追随者副本对外提供服务,由于副本同步是异步的,因此有可能出现追随在副本还没有从领导者副本哪里拉取最新的消息,从而导致客户端看不到最新写入的消息
2.方便实现单调读(Monotonic Reads)
什么是单调读?就是对于一个消费来说,在多次消费之后,它不会看到某条消息一会儿存在一会儿不存在。
如果允许追随者副本提供读服务,那么假设当前有个2个副本追随者F1和F2,他们异步地的拉取领导者副本数据。倘若F1拉去了Leader最新的消息而F2还未及时拉取,那么,此时如果有一个小消费者先从F1服务消息之后又从F2拉取消息,他可能会看到这样的现象:第一次消费时看到的最新消息在第二次消费时不见了,这就不是但调读一致性。但是,如果所有的请求都是由Leader来处理,那么kafka就很容易实现单调读一致性。
In-sync Replicas(ISR)
由于kafka副本是异步进行的数据同步,那么就有可能出现数据库同步不一致的问题,这个时候kafka要明确告诉我们,追随者副本到底在什么条件下才算与Leader同步。
基于这个想法,kafka引入了In-sync Replice ,也就是所谓的ISR副本集合。ISR中的副本都是与Leader同步的副本,不在ISR的追随者副本被认为是与Leader不同步的。
我么首先要明确的是,Leader副本天然就在ISR中,也是就是说,ISR不只是追随者副本集合,它必然包含Leader副本。甚至在某些情况下,ISR中只有Leader这一个副本。
另外,能过进入到ISR的追随者副本满足一定条件,至于是很么条件,先买个关子,我们先来一起看看下面这张图:

这个额标准就是Broker端参数replica.lag.time.max.ms参数。这个参数的含义是Follower副本能够落后Leader副本的最长时间间隔,当前默认是10s。就是说,只要一个Follower副本落后Leader副本的时间不连续超过10s,那么kakfa就认为该Follower副本与Follower是同步的,即使此时Follower副本中保存的消息明显少于Leader副本中的消息。
我们在前面说过,Follower副本位移的工作即使不断的从Leader中拉取消息,然后写入到自己的日志中。如果这个同步过程的速度持续慢于Leader副本的消息写入速度,那么在replica.lag.time.max.ms时间后,此Follower副本就会被认为是与Leader不同步的,因此不能在放入ISR中。此时,kafka会自动收缩ISR集合,将该副本“踢出”ISR。倘若该副本后来慢慢的追上了Leader的进度,那么它是能够重新被追加会ISR的,这表明ISR是一个动态的调整的集合。
Unclean领导者选举(Unclean Leader Election)
kafka把所有不再ISR中的存活的副本都称为非同步副本。通常来说,非同步副本落后Leader太多,因此,如果选举这些非同步副本作为Leader,就可能出现数据的丢失。在kafka中,选举这种副本的过程称为Unclean领导者选举。Broker端参数unclean.leader.election.enable控制是否允许Unclean领导这个选举。
开启Unclean领导者选举会造成数据丢失,但是可以保证服务的可用性。
精彩问答:
问题1:
老师,LEO和HW这两个概念不理解,能不能详细说下,谢谢
作者回复: 一个分区有3个副本,一个leader,2个follower。producer向leader写了10条消息,follower1从leader处拷贝了5条消息,follower2从leader处拷贝了3条消息,那么leader副本的LEO就是10,HW=3;follower1副本的LEO是5。这样说清楚些吗
问题2:
这里有两个问题想请教一下老师:
1.kafka使用replica.lag.max.time.ms来判断是否保留replica在ISR里,那么问题来了,在吞吐量较高的场景下,replica满足这个时间限制,但是LEO相差比较大,leader这时候挂掉,这个replica被选举为新leader,这个时候是不是有一部分数据丢失了?
2.如果问题1确实存在,目前是怎样处理的呢?
作者回复: 是有这种可能,如果你在意这种情况producer端设置acks=all就可以避免了
问题3:
如果我创建的时候指定有三个副本
1. 如果某一个副本所在的broker挂了,kafka会在另一个broker上面新创建一个partition来补充吗?
2. 如果这三个副本所在的broker都挂了,那kafka会不会在一个新的broker上面重新创建一个新的partition来支持读写,还是说,这个partition就不在工作了?
作者回复: 1. 不会
2. 不工作了