【Kafka】原理分析:分区副本机制
1.副本机制
Kafka的每个topic都可以分为多个Partition,并且多个partition会均匀分布在集群的各个节点下。虽然这种方式能够有效的对数据进行分片,但是对于每个partition来说,都是单点的,当其中一个partition不可用的时候,那么这部分消息就没办法消费。
所以kafka为了提高partition的可靠性而提供了副本的概念(Replica),通过副本机制来实现冗余备份。
一主多从:每个分区可以有多个副本,并且在副本集合有1个leader副本,和多个follower副本组成;
- 所有的读写请求都是由leader副本来进行处理。
- follower副本会从leader副本同步消息日志
高可用 ——> 集群架构 ——> 主从模式。kafka集群的主从与zk集群的主从对比:
zookeeper集群
- 是宏观的主从,即整体是一个大集群,leader是相对于所有zookeeper而言
- 主从目的是负载均衡,采用读写分离(主写从读)
- 问题是是主从要强一致可能带来的同步问题;
kafka集群
- 是微观的主从,即每个partition都是一个小集群,leader只是相对于当前分区的副本们而言
- 主从目的是为了备份Partition的数据;因为kafka通过对一个topic下的消息分区存储,再Consumer按区消费,就已经实现了负载均衡
- 问题是数据丢失风险(可能多个分区的leader在一台机器上)
- 分机分布:一般情况下,一个broker最多只有一个副本,同一个分区的多个副本会被均匀分配到集群中不同broker
- 自动选举:当leader副本所在的 broker出现故障后,可以重新选举新的leader副本继续对外提供服务。
通过这样的副本机制来提高 kafka集群的可用性。
2.副本创建
2.1 创建带副本topic
通过下面的命令去创建共3个副本的topic(replication-factor=3 )
sh kafka-topics.sh
--create
--zookeeper localhost:2181
--partitions 3
--replication-factor 3 # 创建3个副本
--topic secondTopic
2.2 获取leader信息
kafka集群中的一个broker中最多只能有一个副本,leader副本所在的broker节点的分区叫leader节点,follower副本所在的broker节点的分区叫follower节点。
如何知道那个各个分区中对应的leader是谁呢?可以从以下两个地方获取
在zookeeper上获取, 比如获取secondTopic第1个分区的状态信息。
get /brokers/topics/secondTopic/partitions/1/state
—> {“controller_epoch”:12,”leader”:0,”version”:1,”leader_epoch”:0,”isr”:[0,1]}
在kafka上获取
sh kafka-topics.sh —zookeeper localhost:2181 —describe —topic test_partition
leader表示当前分区的leader是那个broker-id。下图中。绿色线条的表示该分区中的leader节点。其他节点就为follower
3.副本协同
Kafka分区下有可能有很多个副本(replica)用于实现冗余,从而进一步实现高可用。副本根据角色的不同可分为2类:
leader副本
响应clients端读写请求的副本 ,消息的读写操作都只会由leader节点来接收和处理
注:Producer发来消息后,是leader收到直接返回还是等follower副本同步了再返回,由Producer的acks设置决定
- 负责维护和跟踪ISR中所有follower滞后的状态,若follower副本出现异常,比如宕机、网络断开等原因长时间没有同步到消息,leader将把它踢出去。
follower副本
拉取写入leader副本中的数据,不能响应clients端读写请求(仅作为备份用)
注:同步过程会有一定的延迟,导致follower副本中保存的消息略少于leader副本,但只要未超出阈值都可容忍
- leader挂了以后的选举由follower产生
3.1 同步标志
每个Kafka副本对象都有两个重要的属性:LEO和HW。注意是所有的副本,而不只是 leader副本。
- LEO:即日志末端位移(log end offset),记录了该副本底层日志(log)中下一条消息的位移值。注意是下一条消息!也就是说,如果LEO=10,那么表示该副本保存了10条消息,位移值范围是[0, 9]。另外, leader LEO和follower LEO的更新是有区别的。
- HW:即水位值(hight water)。小于等于HW值的所有消息都被认为是“已备份”的。对于同一个副本对象而言,其HW值不会大于LEO值。同理,leader副本和follower副本的HW更新是有区别的
从生产者发出的一条消息首先会被写入分区的leader 副本,不过还需要等待ISR集合中的所有 follower副本都同步完之后才能被认为已经提交,之后才会更新分区的HW,进而消费者可以消费到这条消息。
随着follower副本不断和leader副本进行数据同步,follower 副本的LEO会主键后移并且追赶到leader副本,这个追赶上的判断标准是当前副本的LEO是否大于或者等于leader副本的HW,这个追赶上也会使得被踢出的follower副本重新加入到ISR集合中。
注:若图中最右侧的follower副本被踢出ISR集合,也会导致这个分区的HW发生变化,变成 3
3.2 ISR队列
ISR(in-Sync replicas , 副本同步队列):包含了leader副本和所有与leader副本保持同步的follower副本(高级备胎们)。
表示目前“可用且消息量与leader相差不多的副本集合,这是整个副本集合的一个子集”。怎么去理解可用和相差不多这两个词呢?具体来说,ISR集合中的副本必须满足两个条件
- 副本所在节点必须维持着与zookeeper的连接
副本最后一条消息的offset与leader副本的最后一条消息的offset之间的差值不能超过指定的阈值
- follower副本把leader副本LEO之前的日志全部同步完成时,则认为follower副本已经追赶上了leader 副本,这个时候会更新这个副本的lastCaughtUpTimeMs标识。
- kafka副本管理器会启动一个副本过期检查的定时任务,这个任务会定期检查当前时间与副本的lastCaughtUpTimeMs的差值是否大于参数 replica.lag.time.max.ms 的值,若大于,则会把这个副本踢出ISR集合
另外,ISR数据保存在Zookeeper的 /brokers/topics/<topic>/partitions/<partitionId>/state
节点中
篇幅原因,关于分区副本协同的更详细过程请看下篇【Kafka】原理分析:详解副本协同流程…
4.副本选举
在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某个Partition的所有Replica都宕机了,就无法保证数据不丢失了。有以下两种选举新leader策略
等待ISR中的任一个Replica“活”过来,并且选它作为Leader
问题:如果一定要等待ISR中的Replica“活”过来,那不可用的时间就可能会相对较长。而且如果ISR中的所有 Replica都无法“活”过来了,或者数据都丢失了,这个Partition将永远不可用。
选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader
问题:选择第一个“活”过来的Replica作为Leader,若这个Replica不是ISR中的Replica,那即使它并不保证已经包含了所有已commit的消息,它也会成为Leader而作为consumer的数据源(所有读写都由Leader完成)
这就需要在可用性和一致性当中作出一个简单的折中。Kafka多采用第一种策略。
还没有评论,来说两句吧...