一、前言
在上一篇文章里咱们探讨了kafka的工作流程、存储机制、分区策略,理分明了生产者生产的数据是怎么存储的以及怎么依据offset去查问数据这类问题:Kafka(一):工作流程、存储机制、分区策略
那么kafka是怎么保证数据可靠性的呢?怎么保障exactly once的呢?在分布式的环境下又是如何进行故障解决的呢?本篇文章咱们就来剖析这个问题。
二、数据可靠性
首先咱们要晓得kafka发送数据的机制:Kafka为了保障producer发送的数据,能牢靠的发送到指定的topic,因而topic的每个partition收到producer发送的数据后,都须要向producer发送ack信息(acknowledgement确认收到),如果producer收到ack,就会进行下一轮的发送,否则从新发送数据。
2.1、正本数据同步策略
咱们晓得kafka的partition是主从构造的,因而当一个topic对应多个partiton时,为了保障leader挂掉之后,能在follower中选举出新的leader且不失落数据,就须要确保follower与leader同步实现之后,leader再发送ack。
大抵图示如下:
这时会产生一个问题也就是正本数据同步策略:多少个follower同步实现之后才发送ack呢?
有两个计划比照如下:
- 半数以上实现同步,就发送ack(长处:提早低;毛病:选举新的leader时,容忍n台节点的故障,须要2n+1个正本)
- 全副实现同步,才发送ack(长处:选举新的leader时,容忍n台节点的故障,须要n+1个正本;毛病:提早高)
咱们晓得kafka采纳零拷贝技术优化数据传输,因而网络提早对kafka的影响较小。然而因为kafka个别都是解决海量数据,在同样为了容忍n台节点故障的前提下,第一种计划须要2n+1个正本,而第二种计划只须要n+1个正本,而Kafka的每个分区都有大量的数据,第一种计划会造成大量数据的冗余,因而kafka采纳了第二种计划:全副实现同步,才发送ack。
2.2、ISR
kafka选用第二种发案来同步正本数据后,可能会呈现一个问题:比方leader收到数据,而后开始向所有的follower同步数据,然而有那么一个或多个follower因为挂掉了之类的起因呈现了故障,不能和leader进行同步,那leader要始终等上来吗?当然不能够,为了解决这个问题,引入了ISR的概念。
ISR是一个动静的in-sync replica set数据集,代表了和leader放弃同步的follower汇合。
当ISR中的follower实现数据的同步之后,leader就会给follower发送ack。如果follower长时间未向leader同步数据,则该follower将被踢出ISR,该工夫阈值由replica.lag.time.max.ms参数设定。Leader产生故障之后,就会从ISR中选举新的leader。
相当于leader只有和ISR里的follower进行数据同步就能够了,呈现故障的会被ISR移出去,复原之后并通过解决还会退出进来。那移出去的follower要通过怎么的解决能力重新加入ISR呢?能够先思考一下,前面故障解决局部会进行剖析。
2.3、ack应答机制
因为数据的重要水平是不一样的,有些能够大量容许失落,心愿快一点解决;有些不容许,心愿稳当一点解决,所以没必要所有的数据处理的时候都等ISR中的follower全副接管胜利。因而kafka解决数据时为了更加灵便,给用户提供了三种可靠性级别,用户能够通过调节acks参数来抉择适合的可靠性和提早。
acks的参数别离能够配置为:0,1,-1。
它们的作用别离是:
- 配置为0:producer不期待broker的ack,这一操作提供了一个最低的提早,broker一接管到还没有写入磁盘就曾经返回,当broker故障时有可能失落数据;
- 配置为1:producer期待broker的ack,partition的leader写入磁盘胜利后返回ack,然而如果在follower同步胜利之前leader故障,那么将会失落数据;
- 配置为-1:producer期待broker的ack,partition的leader和follower全副写入磁盘胜利后才返回ack。然而如果在follower同步实现后,broker发送ack之前,leader产生故障,此时会选举新的leader,然而新的leader曾经有了数据,然而因为没有之前的ack,producer会再次发送数据,那么就会造成数据反复。
三、Exactly Once
3.1、幂等性机制
Kafka在0.11版本之后,引入了幂等性机制(idempotent),指的是当发送同一条音讯时,数据在 Server 端只会被长久化一次,数据不丟不重,然而这里的幂等性是有条件的:
- 只能保障 Producer 在单个会话内不丟不重,如果 Producer 出现意外挂掉再重启是 无奈保障的。因为幂等性状况下,是无奈获取之前的状态信息,因而是无奈做到跨会话级别的不丢不重。
- 幂等性不能跨多个 Topic-Partition,只能保障单个 Partition 内的幂等性,当波及多个Topic-Partition 时,这两头的状态并没有同步。
3.2、实现exactly once
个别对于重要的数据,咱们须要实现数据的准确一致性,对于kafka也就是保障每条音讯被发送且仅被发送一次,不能反复,这就是exactly once。咱们通过上篇文章曾经晓得当acks = -1时,kafka能够实现at least once语义,这时候的数据会被至多发送一次。再配合后面介绍的幂等性机制保证数据不反复,那合在一起就能够实现producer到broker的exactly once语义。
它们的关系能够写成一个公式:idempotent + at least once = exactly once
那怎么配置kafka以实现exactly once呢?
很简略,只需将enable.idempotence属性设置为true,kafka会主动将acks属性设为-1。
四、故障解决
在剖析故障解决之前,咱们须要先晓得几个概念:
- LEO:全称Log End Offset,代表每个正本的最初一条音讯的offset
- HW:全称High Watermark,代表一个分区中所有正本最小的offset,用来断定正本的备份进度,HW以外的音讯不被消费者可见。leader持有的HW即为分区的HW,同时leader所在broker还保留了所有follower正本的LEO。
如下图,是一个topic下的某一个partition里的正本的LEO和HW关系:
留神:只有HW之前的数据才对Consumer可见,也就是只有同一个分区下所有的正本都备份实现,才会让Consumer生产。
它们之间的关系:leader的LEO >= follower的LEO >= leader保留的follower的Leo >= leader的HW >= follower的HW
因为partition是理论的存储数据的构造,因而kafka的故障次要分为两类:follower故障和leader故障。
4.1、follower故障
这部分能够答复后面2.2节最初提到的问题:移出去的follower要通过怎么的解决能力重新加入ISR呢?
通过后面咱们曾经晓得follower产生故障后会被长期踢出ISR,其实待该follower复原后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的局部截取掉,从HW开始向leader进行同步。等该follower的LEO大于等于该分区的HW(leader的HW),即follower追上leader之后(追上不代表相等),就能够重新加入ISR了。
4.2、leader故障
leader产生故障之后,会从ISR中选出一个新的leader,之后,为保障多个正本之间的数据一致性,其余的follower会先将各自的log文件高于HW的局部截掉,而后从新的leader同步数据。
留神:这只能保障正本之间的数据一致性,并不能保证数据不失落或者不反复。
那怎么解决故障复原后,数据失落和反复的问题呢?
kafka在0.11版本引入了Lead Epoch来解决HW进行数据恢复时可能存在的数据失落和反复的问题。
4.3、引入Lead Epoch
leader epoch理论是一对值(epoch, offset),epoch示意leader版本号,offset为对应版本leader的LEO,它在Leader Broker上独自开拓了一组缓存,来记录(epoch, offset)这组键值对数据,这个键值对会被定期写入一个检查点文件。Leader每产生一次变更epoch的值就会加1,offset就代表该epoch版本的Leader写入的第一条日志的位移。当Leader首次写底层日志时,会在缓存中减少一个条目,否则不做更新。这样就解决了之前版本应用HW进行数据恢复时可能存在的数据失落和反复的问题
这就有点像HashMap源码外面的modCount,用来记录整体的更新变动。