一、 kafka的存储机制
kafka
通过topic
来分主题存放数据,主题内有分区,分区可以有多个副本,分区的内部还细分为若干个segment
。所谓的分区其实就是在kafka对应存储目录下创建的文件夹,文件夹的名字是主题名加上分区编号,编号从0
开始。
1. segment
所谓的segment
其实就是在分区对应的文件夹下产生的文件。
一个分区会被划分成大小相等的若干segment
,这样一方面保证了分区的数据被划分到多个文件中保证不会产生体积过大的文件;另一方面可以基于这些segment
文件进行历史数据的删除,提高效率。
一个segment
又由一个.log
和一个.index
文件组成。
.log
.log
文件为数据文件用来存放数据分段数据。.index
.index
为索引文件保存对对应的.log
文件的索引信息。在
.index
文件中,保存了对应.log
文件的索引信息,通过查找.index
文件可以获知每个存储在当前segment
中的offset
在.log
文件中的开始位置,而每条日志有其固定格式,保存了包括offset编号
、日志长度
、key的长度
等相关信息,通过这个固定格式中的数据可以确定出当前offset
的结束位置,从而对数据进行读取。- 命名规则
这两个文件的命名规则为:
partition
全局的第一个segment
从0
开始,后续每个segment
文件名为上一个segment
文件最后一条消息的offset值
,数值大小为64
位,20
位数字字符长度,没有数字用0
填充。
2. 读取数据
开始读取指定分区中某个offset
对应的数据时,先根据offset
和当前分区的所有segment
的名称做比较,确定出数据在哪个segment
中,再查找该segment
的索引文件,确定当前offset
在数据文件中的开始位置,最后从该位置开始读取数据文件,在根据数据格式判断结果,获取完整数据。
二、 可靠性保证
1. AR
在Kafka
中维护了一个AR
列表,包括所有的分区的副本。AR
又分为ISR
和OSR
。
AR = ISR + OSR
AR
、ISR
、OSR
、LEO
、HW
这些信息都被保存在Zookeeper
中。
-
ISR
ISR
中的副本都要同步leader
中的数据,只有都同步完成了数据才认为是成功提交了,成功提交之后才能供外界访问。
在这个同步的过程中,数据即使已经写入也不能被外界访问,这个过程是通过LEO-HW机制
来实现的。 -
OSR
OSR
内的副本是否同步了leader
的数据,不影响数据的提交,OSR
内的follower
尽力的去同步leader
,可能数据版本会落后。
最开始所有的副本都在ISR
中,在kafka
工作的过程中,如果某个副本同步速度慢于replica.lag.time.max.ms
指定的阈值,则被踢出ISR
存入OSR
,如果后续速度恢复可以回到ISR
中。 -
LEO
LogEndOffset
:分区的最新的数据的offset
,当数据写入leader
后,LEO
就立即执行该最新数据。相当于最新数据标识位。 -
HW
HighWatermark
:只有写入的数据被同步到所有的ISR
中的副本后,数据才认为已提交,HW
更新到该位置,HW
之前的数据才可以被消费者访问,保证没有同步完成的数据不会被消费者访问到。相当于所有副本同步数据标识位。
在leader
宕机后,只能从ISR
列表中选取新的leader
,无论ISR
中哪个副本被选为新的leader
,它都知道HW
之前的数据,可以保证在切换了leader
后,消费者可以继续看到HW之前已经提交的数据。
所以LEO
代表已经写入的最新数据位置,而HW
表示已经同步完成的数据,只有HW
之前的数据才能被外界访问。 -
HW截断机制
如果leader
宕机,选出了新的leader
,而新的leader
并不能保证已经完全同步了之前leader
的所有数据,只能保证HW
之前的数据是同步过的,此时所有的follower
都要将数据截断到HW
的位置,再和新的leader
同步数据,来保证数据一致。
当宕机的leader
恢复,发现新的leader
中的数据和自己持有的数据不一致,此时宕机的leader
会将自己的数据截断到宕机之前的hw
位置,然后同步新leader
的数据。宕机的leader
活过来也像follower
一样同步数据,来保证数据的一致性。
2. 生产者可靠性级别
通过以上的讲解,已经可以保证kafka集群内部的可靠性,但是在生产者向kafka集群发送时,数据经过网络传输,也是不可靠的,可能因为网络延迟、闪断等原因造成数据的丢失。
kafka
为生产者提供了如下的三种可靠性级别,通过不同策略保证不同的可靠性保障。
其实此策略配置的就是leader
将成功接收消息信息响应给客户端的时机。
通过request.required.acks
参数配置:
1
:生产者发送数据给leader
,leader
收到数据后发送成功信息,生产者收到后认为发送数据成功,如果一直收不到成功消息,则生产者认为发送数据失败会自动重发数据。当leader宕机时,可能丢失数据。
0
:生产者不停向leader
发送数据,而不需要leader
反馈成功消息。这种模式效率最高,可靠性最低。可能在发送过程中丢失数据,也可能在leader
宕机时丢失数据。
-1
:生产者发送数据给leader
,leader
收到数据后要等到ISR列表
中的所有副本都同步数据完成后,才向生产者发送成功消息,如果一只收不到成功消息,则认为发送数据失败会自动重发数据。这种模式下可靠性很高,但是当ISR
列表中只剩下leader
时,当leader
宕机让然有可能丢数据。
此时可以配置min.insync.replicas
指定要求观察ISR
中至少要有指定数量的副本,默认该值为1
,需要改为大于等于2的值
这样当生产者发送数据给leader
但是发现ISR
中只有leader
自己时,会收到异常表明数据写入失败,此时无法写入数据,保证了数据绝对不丢。
虽然不丢但是可能会产生冗余数据,例如生产者发送数据给leader
,leader
同步数据给ISR
中的follower
,同步到一半leader
宕机,此时选出新的leader
,可能具有部分此次提交的数据,而生产者收到失败消息重发数据,新的leader
接受数据则数据重复了。
3. leader选举
当leader
宕机时会选择ISR
中的一个follower
成为新的leader
,如果ISR
中的所有副本都宕机,怎么办?
有如下配置可以解决此问题:
unclean.leader.election.enable=false
策略1:必须等待ISR列表中的副本活过来才选择其成为leader
继续工作。
unclean.leader.election.enable=true
策略2:选择任何一个活过来的副本,成为leader
继续工作,此follower
可能不在ISR
中。
策略1,可靠性有保证,但是可用性低,只有最后挂了leader
活过来kafka
才能恢复。
策略2,可用性高,可靠性没有保证,任何一个副本活过来就可以继续工作,但是有可能存在数据不一致的情况。
4. kafka可靠性的保证
At most once
:消息可能会丢,但绝不会重复传输。
At least once
:消息绝不会丢,但可能会重复传输。
Exactly once
:每条消息肯定会被传输一次且仅传输一次。
kafka
最多保证At least once
,可以保证不丢,但是可能会重复,为了解决重复需要引入唯一标识和去重机制,kafka
提供了GUID
实现了唯一标识,但是并没有提供自带的去重机制,需要开发人员基于业务规则自己去重。