所谓副本机制,也可以称为备份机制,通常指分布式系统在多台网络互联的机器上保存有相同的数据拷贝。副本机制的好处是什么:
- 提供数据冗余,即部分组件失效,系统依然可以运行,增加整体可用性以及数据持久性。
- 提供高可靠性,支持横向扩展,能够通过增加机器的方式来提升读性能,提高读操作吞吐量。
- 改善数据局部性,允许将数据放入与用户地理位置相近的地方,降低系统延时。
这些好处经常在分布式系统教科书中被提及,但是有些遗憾,目前在kafka中,只能享受副本机制带来的第一个好处,即数据冗余。这一章也会详细解释为什么没能提供第2点和第3点好处,即便如此,副本机制依然是Kafka设计架构的核心所在。
- Kafka中的副本
之前文章谈到过,Kafka是有主题概念的,而每个主题有进一步划分为若干个分区。副本的概念是在分区层级下定义的,每个分区配置有若干个副本。副本,就是一个只能追加写消息的提交日志。同一个分区下的所有副本保存有相同的消息序列,这些副本分散保存在不同Broker上,从而能够对抗Broker宕机带来的数据不可用。在实际生产环境中,每个Broker上都可能保存有不同主题下的不同分区的不同副本。因此,单个Broker上存有成百上千个副本的现象是非常正常的。下面展示一下,比较直观:
既然分区下能配置多个副本,而且副本内容还要一致,那么问题是:该如何确保副本中的所有数据都一致,即当生产者发送消息到某个主题后,消息是如何同步到对应的所有副本中?这个问题最常见的解决方案就是基于领导者的副本机制,kafka就是这样设计的。
基于领导者的副本机制的工作原理如下图所示:
第一,副本分为两类:领导者副本( Leader Replica ) 和 追随者副本 ( Follower Replica )。每个分区在创建时都要选举一个领导者副本,其余的自动成为追随者副本。
第二,Kafka的副本机制比其他分布式系统要严格。在Kafka中,追随者副本不对外提供服务,也就是说,任何一个追随者副本都不能响应消费者和生产者的请求,它唯一的任务就是从领导者副本中拉取消息,并写到提交日志中。这一点,就是前面说的,Kafka没能提供读操作横向扩展以及改善局部性的原因。对于客户端而言,Kafka的Follower副本没有任何作用,既不能像MySQL那样帮助领导者副本“抗读”,也不能实现将某些副本放到离客户端近的地方改善数据局部性。
第三,当领导者副本挂了,或者说所在Broker宕机,Kafka依托于Zookeeper提供的监控功能会实时感知到,并立即开启新一轮领导者选举,从Follower 副本中选一个作为新的Leader。
Kafka的副本机制这样设计的好处:
- 方便实现 "Read-your-writes"。当你使用生产者API向Kafka成功写入消息,马上使用消费者API读取消息。如果允许Follower副本对外提供服务,由于副本机制是异步的,就有可能出现,Follower副本还没从Leader副本拉取最新消息,那么客户端就看不到最新的消息了。
- 方便实现单调读一致性。对于消费者而言,在多次消费时,不会看某条消息一会存在一会不存在。
- In-sync Replicas ( ISR )
前面说过,Follower副本只是定期地异步地从Leader副本拉取数据,就存着不可能与Leader实时同步的风险。那么怎么去应对这种问题,Follower副本在什么样的条件下才算与Leader副本同步。Kafka这里引入 ISR 概念。ISR是一个副本集合,ISR中的副本都是与Leader副本同步的,那么什么样的副本才能进入ISR呢?
首先,Leader副本天生就是在ISR中,甚至在某些情况下,ISR中只包含Leader副本一个。
另外,能够进入ISR的Follower副本要满足一定的条件。"It depends"。Kafka判断Follower副本是否与Leader副本同步,不是看消息数像差多少,而是另有玄机。这个玄机就是,Broker端参数 relpica.lag.time.max.ms,它表示Follower副本能落后Leader副本最长时间间隔,当前默认值是10秒。而Producer端参数acks=all,是保证ISR 中所有Follower副本同步。只要Follower副本落后Leader副本的时间不连续超过10秒,就认为是同步的,即使保存的消息明显少于Leader副本中的消息。而且在Follower副本不断地从Leader副本同步消息过程中,一旦消息落后时间超过这个参数值,就会被“踢出”ISR。然后如果,Follower副本落后Leader副本消息的时间又赶上了,还是能再回到 ISR 中的。
- Unclean领导者选举 ( Leader Election )
既然ISR可以动态调整,那么就有可能出现ISR为空,这样就说明Leader副本挂了,Kafka需要重新选举一个新的Leader。Kafka把所有不在ISR中的副本都称为非同步副本。如果把非同步副本选举为Leader副本,那就会造成消息丢失,因为这些副本中保存的消息远远落后于老Leader副本中的消息,选举这类副本作为Leader副本的过程就叫这Unclean领导者选举。Broker端参数 unclean.leader.election.enable控制着是否允许Unclean领导者选举。
开启这种选举方式,会造成选举丢失,但是使得分区一直存在,不至于停止对外服务了,这样其实提高了高可用性。反过来,若关闭它,虽然避免了这种情况的消息丢失,但是降低了系统的高可用性。如果你听说过CAP理论,就知道分布式系统只能同时满足一致性 ( Consistency )、可用性 ( Availability )、分区容错性 ( Partition tolerance )中的两个。显然,Kafka赋予你 C 和 A的权利。
注意,最佳时间推荐你不要开启这种选举方式,因为可以通过其他方式提升高可用性,如果单纯为了提神高可用性,而牺牲一致性是得不偿失的。
后面篇幅,会接着从源代码角度分析副本机制。
标注:这个系列文章是本人在极客时间专栏---kafka核心技术与实战中的学习笔记
https://time.geekbang.org/column/article/101171