Kafka整理

kafka目前是只能保证一个分区内的数据是有序的。producer按照消息的顺序进行发送,相同key的消息能够hash到相同的分区,consumer能够按照消息的顺序进行消费。

 

过半仅仅需要最快的n+1的写入成功即可判定为成功,而isr则需要2n+1的写入成功才算成功。同时isr是动态变化的过程,一旦跟不上或者跟上了都会离开或者加入isr列表。isr列表越小写入速度就会加快。过半更多会牺牲可用性(挂掉一半以上就不可用了)来增强数据的一致性,而isr会牺牲一致性来增强可用性(挂掉一半以上扔可使用,但是存在丢数据的问题)

数据可靠性,每个topic有多个partition,每个partition又有多个复本。

kafka集群有多个kafka实例组成,每个实例(server)成为broker。无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。

Topics/logs

一个Topic可以认为是一类消息,每个topic将被分成多个partition(区),不同的topic之间是相互独立的。每个partition在存储层面是append log文件。任何发布到此partition的消息都会被直接追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型数字,它是唯一标记一条消息。它唯一的标记一条消息。

以顺序追加的方式向各个分区中写入消息,每个分区都是不可变的消息队列。分区中的消息都是以k-v形式存在。

l  k表示offset,称之为偏移量,一个64位整型的唯一标识,offset代表了Topic分区中所有消息流中该消息的起始字节位置。

l  v就是实际的消息内容,每个分区中的每个offset都是唯一存在的,所有分区的消息都是一次写入,在消息未过期之前都可以调整offset来实现多次读取。

kafka并没有提供其他额外的索引机制来存储offset,因为在kafka中几乎不允许对消息进行“随机读写”。 不支持随机删除以及随机访问。已经生产的消息但是消费者可以通过重置offset的方式来访问已经消费过的数据。并且Producer,broker和consumer共享的标准化二进制消息格式,这样数据块就可以在它们之间自由传输,无需转换,降低了字节复制的成本开销。

由于KAFKA是基于JVM的,并且任何与Java内存使用打过交道的人都知道两件事:

l  对象的内存开销非常高,通常是实际要存储数据大小的两倍;

l  随着数据的增加,java的垃圾收集也会越来越频繁并且缓慢。

基于此,使用文件系统,同时依赖页面缓存就比使用其他数据结构和维护内存缓存更有吸引力:

l  不使用进程内缓存,就腾出了内存空间,可以用来存放页面缓存的空间几乎可以翻倍。

l  如果KAFKA重启,进行内缓存就会丢失,但是使用操作系统的页面缓存依然可以继续使用。

可能有人会问KAFKA如此频繁利用页面缓存,如果内存大小不够了怎么办?

KAFKA会将数据写入到持久化日志中不是刷新到磁盘,只是转移到了内核的页面缓存,直接利用操作系统的页缓存来实现文件到物理内存的直接映射。完成映射之后再在适当时候会被同步到硬盘上。在生产端和消费端分别采取的push和pull的方式,可以认为KAFKA是个无底洞,有多少数据可以使劲往里面推送,消费端则是根据自己的消费能力,需要多少数据自己过来KAFKA这里拉取,KAFKA能保证只要这里有数据,消费端需要多少,都尽可以自己过来拿。

KAFKA高吞吐率性能揭秘

生产端API发布消息到1个或多个Topic(主题)的一个(保证数据的顺序)或者多个分区(并行处理,但不一定保证数据顺序)。Topic可以简单理解成一个数据类别,是用来区分不同数据的。

消息顺序保障

kafka对消息的重复、丢失、错误以及顺序型没有严格的要求kafka提供at-least-oncedelivery,即当consumer宕机后,有些消息可能会被重复delivery。 因为每个partition只会被consumer group内的一个consumer消费,故kafka保证每个partition内的消息会被顺序的订阅。 Kafka为每条消息为每条消息计算CRC校验,用于错误检测,crc校验不通过的消息会直接被丢弃掉。

针对部分消息有序(message.key相同的message要保证消费顺序)场景,可以在producer往kafka插入数据时控制,同一key分发到同一partition上面。每个partition是固定分配给某个消费者进行消费的,所以对于在同一个分区的消息来说,是严格有序的。

顺序消息需要注意以下几个问题:AB--BA问题

1.    消息重试对顺序消息的影响

对于一个有着先后顺序的消息A、B,正常情况下应该是A先发送完成后再发送B,但是在异常情况下,在A发送失败的情况下,B发送成功,而A由于重试机制在B发送完成之后重试发送成功了。这时对于本身顺序为AB的消息顺序变成了BA

2.    消息producer发送逻辑的控制

消息producer在发送消息的时候,对于同一个broker连接是存在多个未确认的消息在同时发送的,也就是存在以上场景1说到的情况,虽然A和B消息是顺序的,但是由于存在未知的确认关系,有可能存在A发送失败,B发送成功,A需要重试的时候顺序关系就变成了BA。简之一句就是在发送B时A的发送状态是未知的。

严格的顺序消费还需要以下参数支持:max.in.flight.requests.per.connection,应该将其设置为1,保证在后一条消息发送前,前一条的消息状态已经是可知的。

稀疏索引

从上图中可以看出,索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。以索引文件中元数据3,497为例,依次在数据文件中表示第3个message(在全局partiton表示第368772个message)、以及该消息的物理偏移地址为497。

      例如读取offset=368776的message,需要通过下面2个步骤查找。

       第一步查找segment file,00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.同样,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset 二分查找文件列表,就可以快速定位到具体文件。当offset=368776时定位到00000000000000368769.index|log。

       第二步通过通过segment file查找message,通过第一步定位到的00000000000000368769.index,368776-368769=7,表示在索引文件中第7个message,于是定位到了6,1407记录,之后在00000000000000368769.log中找到1407物理偏移地址的message是Message368775,然后顺利查找直到offset=368776为止。

       这样做的优点,index文件中不用为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。通过mmap可以直接内存操作,稀疏索引为数据文件的每个对应message设置一个元数据指针,它比稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。

 

批量压缩

在很多情况下,系统的瓶颈不是CPU或磁盘,而是网络带宽,对于需要在广域网上的数据中心之间发送消息的数据流水线尤其如此。所以数据压缩就很重要。可以每个消息都压缩,但是压缩率相对很低。所以KAFKA使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩。

KAFKA允许使用递归的消息集合,批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩。KAFKA支持Gzip和Snappy压缩协议。

文件消除

kafka和JMS(Java MessageService)实现(activeMQ)不同的是:即使消息被消费后仍然不会被立即删除。日志文件将会根据broker中的配置要求,保留一定的时间之后再删除:比如log文件保留2天,那么两天后文件会被清除,无论其中的消息是否被消费。kafka通过这种简单的手段来释放磁盘空间,以及减少消息消费之后对文件内容改动的磁盘IO开支。Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。

通过索引信息可以快速定位message和确定response的最大大小。

通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。

通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。

KAFKA数据可靠性

KAFKA的消息保存在Topic中,Topic可分为多个分区,为保证数据的安全性,每个分区又有多个Replia。

多分区的设计的特点:

1.为了并发读写,加快读写速度;

2.是利用多分区的存储,利于数据的均衡;

3.是为了加快数据的恢复速率,一但某台机器挂了,整个集群只需要恢复一部分数据,可加快故障恢复的时间

每个Partition分为多个Segment,每个Segment有.log和.index 两个文件,每个log文件承载具体的数据,每条消息都有一个递增的offset,Index文件是对log文件的索引,Consumer查找offset时使用的是二分法根据文件名去定位到哪个Segment,然后解析msg,匹配到对应的offset的msg。

KAFKA数据恢复

每个Partition会在磁盘记录一个RecoveryPoint,,记录已经flush到磁盘的最大offset。当broker 失败重启时,会进行loadLogs。首先会读取该Partition的RecoveryPoint,找到包含RecoveryPoint的segment及以后的segment, 这些segment就是可能没有完全flush到磁盘segments。然后调用segment的recover,重新读取各个segment的msg,并重建索引。每次重启KAFKA的broker时,都可以在输出的日志看到重建各个索引的过程。

查询效率

Kafka解决查询效率的手段之一是将数据文件划分为多个segment,比如有100条Message,它们的offset是从0到99。假设将数据文件分成5段,第一段为0-19,第二段为20-39,以此类推,每段放在一个单独的数据文件里面,数据文件以该段中最小的offset命名。这样在查找指定offset的Message的时候,用二分查找就可以定位到该Message在哪个段中

数据同步

当Producer向Broker发送数据时,可以通过request.required.acks参数设置数据可靠性的级别。

0: 表示Producer从来不等待来自broker的确认信息。这个选择提供了最小的时延但同时风险最大(因为当server宕机时,数据将会丢失)。

1:表示获得Leaderreplica已经接收了数据的确认信息。这个选择时延较小同时确保了server确认接收成功。

-1:Producer会获得所有同步replicas都收到数据的确认。同时时延最大,然而,这种方式并没有完全消除丢失消息的风险,因为同步replicas的数量可能是1。如果你想确保某些replicas接收到数据,那么你应该在Topic-level设置中选项min.insync.replicas设置一下。

仅设置 acks= -1 也不能保证数据不丢失,当ISR列表中只有Leader时,同样有可能造成数据丢失。要保证数据不丢除了设置 acks=-1 ,还要保证ISR的大小大于等于2。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值