KafKa-生产者发送数据可靠性
为保证producer发送的数据,能可靠的发送到指定的 topic,topic的每个partition收到producer发送的数据后,都需要向producer发送ack(acknowledgement确认收到 ),如果producer收到 ack 就会进行下一轮的发送,否则重新发送数据。
那么何时发送ack就是一个值得探讨的问题,因为为了提高集群的可靠性,每个topic的每个分区都有一定数量的副本数,发送ack之前必须要考虑这些副本中leader和follower的数据同步问题,必须确保follower中的数据和leader是保持同步的,这样才能确保在leader出现意外挂掉之后,在其他的follower中能够选出替代原有leader的副本作为新的leader。
副本的同步策略主要有两种,一种是所有的副本半数以上完成同步,就发送ack;另一种是所有副本全部完成同步,才能发送ack。它们的优缺点如下表所示:
KafKa选择了第二种同步策略,原因如下:
1. 同样为了容忍n 台节点的故障,第一种方案需要2n+1 个副本,而第二种方案只需要n+1个副本,而Kafka 的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。
2. 虽然第二种方案的网络延迟会比较高,但网络延迟对Kafka 的影响较小。
但是第二种策略也有缺陷,那就是如果所有副本中只要有一个follower挂掉了,那么多有的其他副本就无法发送ack。为了解决这个问题,就提出了一个解决方案--ISR(in-sync replica set),意思为和leader保持同步的follower集合。当这个集合中的follower完成数据的同步以后,leader就会给follower发送ack。如果follower长时间没有向leader同步数据,则该follower将被提出ISR,这个时间阈值由replica.lag.time.max.ms参数设定。Leader发送故障之后,就会从ISR中选举出新的leader。
follower能够进入ISR主要考虑的因素是leader和follower的通讯时间以及follower已经同步的数据量(越多越好),在KafKa的高版本之后,去掉了第二个条件,只保留了第一个。
对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等ISR中的follower全部接收成功。所以KafKa为用户提供了三种可靠性级别,用户根据可靠性和延迟的要求进行选择,对应的三个级别如下:
0:producer 不等待broker 的ack,这一操作提供了一个最低的延迟,broker 一接收到还没有写入磁盘就已经返回,当broker 故障时有可能丢失数据;
1:producer 等待broker 的ack,partition 的leader 落盘成功后返回ack,如果在follower同步成功之前leader 故障,那么将会丢失数据;
-1(all):producer 等待broker 的ack,partition 的leader 和follower 全部落盘成功后才返回ack。但是如果在follower 同步完成后,broker 发送ack 之前,leader 发生故障,那么会造成数据重复。
假设一个场景,对于某个分区的所有副本,leader含有十条数据,follower1同步了9条,follower2同步了8条,当follower1和follower2未完成同步的时候,leader挂掉了,此时两个follower需要重新选举leader,如果follower2成为了leader,那么此时就产生了数据不一致的问题。解决方法如下:
故障处理细节
LEO:指的是每个副本最大的offset;
HW:指的是消费者能见到的最大的offset,ISR 队列中最小的LEO。
如果leader发生故障挂掉了,那么会从ISR中选出一个新的leader,之后,为了保证多个副本之间的数据一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader同步数据。
如果是follower发生故障,那么这个follower会先被临时剔除ISR,等待该follower恢复之后,follower 会读取本地磁盘记录的上次的HW,并将log文件高于HW 的部分截取掉,从HW开始向leader 进行同步。等该follower的LEO大于等于该Partition的HW,即follower追上leader之后,就可以重新加入ISR 了。
需要注意的是,这种方法只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。一致性包括消费一致性,即消费者见到的最大的offset是一致的;存储一致性,所有副本保持数据同步。