副本机制深入
# 一、副本定义
Kafka的层级是:主题 - 分区 - 副本,副本的概念实际上是在分区层级下定义的,每个分区配置有若干个副本。
所谓副本,本质上就是只能追加消息的的提交日志,同一分区下的副本保存相同的消息,这些副本分散在不同的broker上,来对抗部分 Broker 宕机带来的数据不可用。

# 二、副本角色
数据的同步不可能一蹴而就,如何确保数据已经同步完毕呢?这就需要领导者(Leader-based)的副本机制:

- 在 Kafka 中,副本分成两类:领导者副本(Leader Replica)和追随者副本(Follower Replica)。每个分区在创建时都要选举一个副本,称为领导者副本,其余的副本自动称为追随者副本。
- Kafka 的副本机制比其他分布式系统要更严格一些。在 Kafka 中,追随者副本是不对外提供服务的。这就是说,任何一个追随者副本都不能响应消费者和生产者的读写请求。所有的请求都必须由领导者副本来处理。
- 当领导者副本挂掉了,或者说领导者副本所在的 Broker 宕机时,Kafka 依托于 ZooKeeper 提供的监控功能能够实时感知到,并立即开启新一轮的领导者选举,从追随者副本中选一个作为新的领导者。
追随者副本不处理客户端请求,它唯一的任务就是从领导者副本异步拉取消息,并写入到自己的提交日志中,从而实现与领导者副本的同步。
Kafka副本机制特殊在于只有领导副本提供服务,随从副本负责数据的同步,这样有如下好处:
- Read-your-writes:由于数据同步是一个异步操作,如果随从副本提供读服务,会存在最新消息延迟问题,而只有一个提供服务,就能实现写完之后,立刻能读到自己最新的消息。
- Monotonic Reads(单调读):假设两个随从副本提供读服务,由于两个服从副本同步消息的速度不一样,对于同一个消费者而言,可能会存在读取一个副本的时候有消息,读取另一个副本的时候没消息,而kafka只有主副本提供读写服务,就不会存在这个问题。
# 三、副本的优势
对于一般分布式系统,副本机制有如下好处:
- 提供数据冗余:即使系统部分组件失效,系统依然能够继续运转,因而增加了整体可用性以及数据持久性。
- 提供高伸缩性:支持横向扩展,能够通过增加机器的方式来提升读性能,进而提高读操作吞吐量。
- 改善数据局部性:允许将数据放入与用户地理位置相近的地方,从而降低系统延时。
但是Kafka的副本机制,由于存在领导副本和跟随副本,而且只有领导副本对外提供服务,所以只能提供数据冗余,而提供高伸缩性和改善数据局部性无法提供。
# 四、AR、ISR、OSR
AR:分区中所有副本称为 ARISR:所有与主副本保持一定程度同步的副本(包括主副本)称为 ISROSR:与主副本滞后过多的副本组成 OSR
其中,ISR 不只是追随者副本集合,它必然包括 Leader 副本。甚至在某些情况下,ISR 只有 Leader 这一个副本。
那么如何判断副本是否能够进入ISR呢:这个标准就是 Broker 端参数 replica.lag.time.max.ms 参数值。这个参数的含义是 Follower 副本能够落后 Leader 副本的最长时间间隔,当前默认值是 10 秒。

在上图中,虽然Follower 2副本能落后leader副本,但是只是消息数量相当落后,如果Follower 2同步速率更快,在replica.lag.time.max.ms间隔时间之内就能完成所有数据的同步,那么Follower 2也是在ISR集合中的。
通过上面的分析,Follower 副本唯一的工作就是不断地从 Leader 副本拉取消息,然后写入到自己的提交日志中。如果这个同步过程的速度持续慢于 Leader 副本的消息写入速度,那么在 replica.lag.time.max.ms 时间后,此 Follower 副本就会被认为是与 Leader 副本不同步的,因此不能再放入 ISR 中。此时,Kafka 会自动收缩 ISR 集合,将该副本“踢出”ISR。
# 五、领导者选举(Unclean Leader Election)
如果ISR为空,说明领导副本挂掉了,那么kafka需要重新选举一个新的leader,而非同步副本落后 Leader 太多,如果选择这些副本作为新副本就可能出现数据的丢失。在kafka配置中,有一个unclean.leader.election.enable来控制是否允许 Unclean 领导者选举。
- 开启 Unclean 领导者选举可能会造成数据丢失,但好处是,它使得分区 Leader 副本一直存在,不至于停止对外提供服务,因此提升了高可用性。反之,禁止 Unclean 领导者选举的好处在于维护了数据的一致性,避免了消息丢失,但牺牲了高可用性。
- 不开启,相当于这些落后副本无法参与竞选。
建议不要开启它,毕竟我们还可以通过其他的方式来提升高可用性。如果为了这点儿高可用性的改善,牺牲了数据一致性,那就非常不值当了。