kafka(数据可靠性深度解读)

一、概述

Kafka是一个分布式的消息系统。
Kafka作为一个商业级消息中间件,消息可靠性的重要性可想而知。需要考虑:
如何确保消息的精确传输?
如何确保消息的准确存储?
如何确保消息的正确消费?

二、Kafka体系架构

在这里插入图片描述

2.1 Kafka架构中的主要角色

Broker: 消息B服务器,一个Kafka节点就是一个broker,一个或者多个broker可以组成一个kafka集群
**Topic:**主题,发布订阅模式下的消息统一汇聚地,不同生产者向topic发送消息,由MQ服务器发送到不同的订阅者,实现消息的广播
**Producer:**消息生产者,向Broker发送消息的客户端
**Consumer:**消息消费者,从Broker读取消息的客户端
ConsumerGroup: 每个Consumer属于一个特定的Consumer Group,一条消息可以发送到多个不同的Consumer Group,但是一个Consumer Group中只能有一个Consumer能够消费该消息
**Partition:**物理上的概念,一个topic可以分为多个partition,每个partition内部是有序的

2.2 Topic 和 Partition

1) Topic:
一个Topic可以认为是一类消息
2) Partition:
每个Topic可以被划分为多个partition,每个partition存储层面是追加.log文件。任何发布到此partition的消息都将会被追加到log文件的尾部,每条消息在文中的位置称位offset(偏移量),它是Long类型的,它能够唯一标记一条消息。
3) 存储文件类型:
每一个log的文件名都代表了它的偏移量,文件夹下有两个同名文件,log类型和index类型。通过二分查找可以快速定位偏移量。
4) Kafka是磁盘读写为什么比内存快?
每条消息都被追加到partition中的,且是顺序读写磁盘,效率高(随机写会导致磁头不停地换道,造成效率的极大降低;顺序写磁头几乎不用换道,或者换道的时间很短)
5) 为什么要分区?:
分区体现了Kafka的水平扩展性能
每一条消息被发送到broker中,会根据partition规则选择被存储到哪一个partition。如果partition规则设置的合理,所有消息可以均匀分布到不同的partition里,这样就实现了水平扩展。(如果一个topic对应一个文件,那这个文件所在的机器I/O将会成为这个topic的性能瓶颈,而partition解决了这个问题)。在创建topic时可以在$KAFKA_HOME/config/server.properties中指定这个partition的数量(如下所示),当然可以在topic创建之后去修改partition的数量。
6) 消息自动分区的规则:
在发送一条消息时,可以指定这个消息的key,producer根据这个key和partition机制来判断这个消息发送到哪个partition。partition机制可以通过指定producer的partition.class这一参数来指定,该class必须实现kafka.producer.Partitioner接口。

三、高可靠性存储分析

Kafka的高可靠性的保障来源于其健壮的副本(replication)策略。通过调节其副本相关参数,可以使得Kafka在性能和可靠性之间运转的游刃有余。
下面从
文件存储 => 复制原理和同步方式 => ISR,HW,leader选举=> 数据可靠性和持久性
几个维度来丰富到kafka的认知

3.1 Kafka文件存储机制

1)Kafka中消息是以topic进行分类的,生产者通过topic向kafka broker发送消息,消费者通过topic读取数据。
2) topic在物理层面以partition为分组,一个topic可以分成多个partition

那么topic以及partition又是怎么存储的呢?partition还可以细分为segment,一个partition物理上由多个segment组成,那么这些segment又是什么呢?
在Kafka/config/server.properties 中,配置Kafka消息文件存储目录:
log.dirs=/tmp/kafka-logs
创建一个topic,并设置分区数:
kafka-topics.sh --create --zookeeper localhost:2181 --partitions 4 --topic topic_zzh_test --replication-factor 1
那么在kafka-logs目录中可以看到四个目录:

drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-0
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-1
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-2
drwxr-xr-x 2 root root 4096 Apr 10 16:10 topic_zzh_test-3

在Kafka文件存储中,同一个topic下有多个不同的partition,每个partiton为一个目录,partition的名称规则为:topic名称+有序序号,第一个序号从0开始计,最大的序号为partition数量减1,partition是实际物理上的概念,而topic是逻辑上的概念。

3)partition还可以分成多个segment,一个partition物理上由多个segment组成
如果就以partition为最小存储单位,当生产者不断发送消息,必然会引起partition文件的无限扩增,这样对于消息文件的维护以及已经被消费的消息的清理带来严重的影响,所以这里以segment为单位又将partition细分。每个partition(目录)相当于一个巨型文件被平均分配到多个大小相等的segment(段)数据文件中(每个segment 文件中消息数量不一定相等)这种特性也方便old segment的删除,即方便已被消费的消息的清理,提高磁盘的利用率。每个partition只需要支持顺序读写就行,segment的文件生命周期由服务端配置参数(log.segment.bytes,log.roll.{ms,hours}等若干参数)决定。

segment文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为segment索引文件和数据文件。这两个文件的命令规则为:partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值,数值大小为64位,20位数字字符长度,没有数字用0填充,如下:

00000000000000000000.index
00000000000000000000.log
00000000000000170410.index
00000000000000170410.log
00000000000000239430.index
00000000000000239430.log

“.index”索引文件存储大量的元数据
“.log”数据文件存储大量的消息
索引文件中的元数据指向对应数据文件中message的物理偏移地址。
其中以“.index”索引文件中的元数据[3, 348]为例,在“.log”数据文件表示第3个消息,即在全局partition中表示170410+3=170413个消息,该消息的物理偏移地址为348。

3.2 复制原理和同步方式

为了提高消息的可靠性,Kafka每个topic的partition有N个副本(replicas),其中N(大于等于1)是topic的复制因子(replica fator)的个数。Kafka通过多副本机制实现故障自动转移,当Kafka集群中一个broker失效情况下仍然保证服务可用。在Kafka中发生复制时确保partition的日志能有序地写到其他节点上,N个replicas中,其中一个replica为leader,其他都为follower, leader处理partition的所有读写请求,与此同时,follower会被动定期地去复制leader上的数据。
Kafka提供了数据复制算法保证,如果leader发生故障或挂掉,一个新leader被选举并被接受客户端的消息成功写入。Kafka确保从同步副本列表中选举一个副本为leader,或者说follower追赶leader数据。
在这里插入图片描述

Kafka的副本(replication)策略:

名词解释:
HW:HW是HighWatermark的缩写,俗称高水位。是指consumer能够看到的此partition的位置,取一个partition对应的ISR中最小的LEO作为HW,consumer最多只能消费到HW所在的位置。另外每个replica都有HW,leader和follower各自负责更新自己的HW的状态。(从后面的学习来看,每个replication维护的HW有可能不同,当他没有和leader取数据同步HW的时候,leader挂掉了,不能把自己回到HW的状态等待生产者重新发送消息,这样有可能会造成数据丢失)
LEO:LogEndOffset的缩写,表示每个partition的log最后一条Message的位置,最低的partition的LEO就是HW
ISR:In-Sync Replicas的缩写,表示副本同步队列,默认情况下Kafka的replica数量为1,即每个partition都有一个唯一的leader,为了确保消息的可靠性,通常应用中将其值(由broker的参数offsets.topic.replication.factor指定)大小设置为大于1,比如3。由leader维护ISR列表,follower从leader同步数据有一些延迟,包括延迟时间replica.lag.time.max.ms,(延迟条数replica.lag.max.messages在0.10.x版本中删除),超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表(说人话:ISR中存放了leader和同步比较快的follower,如果follower跟不上了延迟较高就踢出去,OSR中的follower重新跟上了延迟较低再加入回来,ISR+OSR=AR。所有的副本(replicas)统称为Assigned Replicas,即AR,ISR是其子集)
在这里插入图片描述
由此可见:
Kafka的复制机制既不是完全的同步复制,也不是单纯的异步复制。
同步复制要求所有能工作的follower都复制完,这条消息才会被commit,这样的方式极大的影响了吞吐率
异步复制方式下,follower异步的从leader复制数据,数据只要被leader写入log就被认为已经commit,这种情况下如果follower都还没有复制完,落后于leader时,突然leader宕机,则会丢失数据。
Kafka的这种使用ISR的方式则很好的均衡了确保数据不丢失以及吞吐率。

3.3 数据可靠性和持久性保证

当producer向leader发送数据时,可以通过request.required.acks参数来设置数据可靠性的级别:
1(默认):这意味着producer在ISR中的leader已成功收到数据并得到确认。如果leader宕机了,则会丢失数据。
0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
-1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。但是这样也不能保证数据不丢失,比如当ISR中只有leader时(前面ISR那一节讲到,ISR中的成员由于某些情况会增加也会减少,最少就只剩一个leader),这样就变成了acks=1的情况。

3.4 Zookeeper的Leader选举

Kafka的提交机制:
一条消息只有被ISR中的所有follower都从leader复制过去才会被认为已提交。
(避免了部分数据被写进了leader,还没来得及被任何follower复制就宕机了,而造成数据丢失)
在生产者设置了acks=-1时,生产者会等待消息commit
(这种机制确保了只要ISR中有一个或者以上的follower,一条被commit的消息就不会丢失)

如何选举出新的leader?
一个基本的原则:如果leader不在了,新的leader必须拥有原来的leader commit的所有消息
master/slave主从架构常用的选举方式是"少数服从多数(过半通过)原则",Kafka使用的并不是这种选举方式。这种方式的缺点:在生产环境下为了保证较高的容错率,必须要有大量的副本,而大量的副本又会在大数据量下导致性能的急剧下降。

Kafka使用的选举方式:
Kafka在Zookeeper中为每一个partition动态维护一个ISR,这个ISR里面的所有replica都是跟上leader的(若在设定的延时差内没有跟上,会被踢出),只有ISR内的成员才有被选为leader的可能(unclean.leader.election.enable=false)在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失。
特殊情况:
1.等待ISR中任意一个replica“活”过来,并且选它作为leader -> 不可用的时间就可能会相对较长
2.选择第一个“活”过来的replica(并不一定是在ISR中)作为leader -> 不保障已经包含了所有已commit的消息(unclean.leader.election.enable=true 也是Kafka的默认参数设置)

3.5 Kafka的发送模式:

由producer端的配置参数producer.type来设置,这个参数指定了在后台线程中消息的发送方式是同步的还是异步的。
默认是同步的方式,即producer.type=sync
异步的模式,即producer.type=async,可以是producer以batch的形式push数据,这样会极大的提高broker的性能,但是这样会增加丢失数据的风险。
如果需要确保消息的可靠性,必须要将producer.type设置为sync。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值