摘要
Kafka能够存储大量的消息并将其持久化到磁盘上,那么必然是需要解决消息丢失的问题的。其实之前的基础知识 中已经简单介绍了下分区和副本的概念,本文主要介绍了KafKa的副本同步机制,详细讲解了Kafka是如何实现不丢失消息的
涉及名词
Replica:副本
ISR:同步状态的备份的集合 (a set of in-sync replicas)
HW:replica高水印值,副本中最新一条已提交消息的位移
LEO:日志末端位移
如果疑议,欢迎gitee 提 issue,共同进步
日志备份
如果想要数据不出现单点丢失的问题,那么就是一定要做备份,而kafka 的消息都是存储在日志文件中的,所以Kafka的备份叫做日志备份,也就是副本机制。
备份有两种方式
- 同步备份,即所有副本同步好了之后,才算提交成功,这种方式及其影响性能
- 异步备份,即Leader节点备份好了之后就算成功,此时如果Leader在其他节点同步之前挂了,那么消息有可能丢失
为了解决这个问题,开发者们在Kafka中引入了ISR(同步状态的备份的集合),这个集合包含着和副本Leader保持着高度一致的副本。更直白的说,就是只有在这个集合内的副本,才能够在Leader异常时,参与竞选Leader。与之对应的,就是那些不在这个集合里的副本,为了便于区分,ISR之外的副本叫做OSR(Out-of Replica)。
在基础概念的文章中,我们提到Producer会向Kafka集群发送消息,但是并没有涉及细节。Kafka集群的处理逻辑如下
- Broker Controller收到请求
- 发送给分区Leader副本,Leader正常存好(LEO+1)
- Broker Contriller 通知所有其他副本从Leader 获取消息
- 假设此时有两个副本R1,R2,并且都在ISR内
- R1 获取了leader 的所有消息,二者完全一致,则R1 加入ISR,同时Leader 标记HW+1,标示HW之前的消息都可被消费了,因为已经存好了
- R2 获取Leader 的一部分消息,则R2被踢出ISR集合,之后再找机会慢慢同步
- 回复Producer,消息存储成功
所以分区的逻辑架构整体看起来是这样的,而每一个副本分布在不同的Broker上,则保证了不会因为单台机器异常,导致数据丢失

异常逻辑
正常选举
主要是指Leader 异常了怎么选举新的Leader,逻辑很简单,就是由ISR内的副本进行抢占式选举,逻辑如下
- Broker Controller 发现Leader副本异常(没有定时汇报,无法通信),同时所有ISR副本进行选举
- ISR 内的副本去ZK上抢占注册
- Broker Controller 会把新Leader的信息同步给所有副本
异常选举
ISR 为空怎么办?这种状态下的选举叫做 UnClean 选举,可以通过 Broker 的配置 unclean.leader.election.enable 进行开关控制,一般不建议打开,因为这种情况下,数据很可能不一致。不过可以保证集群可用,这就涉及到CAP了,需要权衡
总结
Kafka的备份机制既不是同步备份,也不是异步备份,而是做出了权衡,既保证了性能,又保证了效率。
其他细节
- ISR 判断标准
- replica.lag.time.max.ms Follower 副本能够落后 Leader 副本的最长时间间隔,当前默认值是 10 秒。这就是说,只要一个 Follower 副本落后 Leader 副本的时间不连续超过 10 秒,那么 Kafka 就认为该 Follower 副本与 Leader 是同步的,即使此时 Follower 副本中保存的消息明显少于 Leader 副本中的消息。
- ISR的管理最终都会反馈到Zookeeper节点上。具体位置为:**/brokers/topics/[topic]/partitions/[partition]/state。**目前有两个地方会对这个Zookeeper的节点进行维护:
- Controller来维护:Kafka集群中的其中一个Broker会被选举为Controller,主要负责Partition管理和副本状态管理,也会执行类似于重分配partition之类的管理任务。在符合某些特定条件下,Controller下的LeaderSelector会选举新的leader,ISR和新的leader_epoch及controller_epoch写入Zookeeper的相关节点中。同时发起LeaderAndIsrRequest通知所有的replicas。
- Leader来维护:leader有单独的线程定期检测ISR中follower是否脱离ISR, 如果发现ISR变化,则会将新的ISR的信息返回到Zookeeper的相关节点中。
- 同步相关的配置
server 配置
rerplica.lag.time.max.ms=10000
# 如果leader发现flower超过10秒没有向它发起fech请求,那么leader考虑这个flower是不是程序出了点问题
# 或者资源紧张调度不过来,它太慢了,不希望它拖慢后面的进度,就把它从ISR中移除。
topic配置
min.insync.replicas=1 # 需要保证ISR中至少有多少个replica
producer配置
request.required.asks=0
# 0:相当于异步的,不需要leader给予回复,producer立即返回,发送就是成功,
那么发送消息网络超时或broker crash(1.Partition的Leader还没有commit消息 2.Leader与Follower数据不同步),
既有可能丢失也可能会重发
# 1:当leader接收到消息之后发送ack,丢会重发,丢的概率很小
# -1:当所有的follower都同步消息成功后发送ack. 丢失消息可能性比较低