4.Kafka消息处理

4.Kafka消息处理

更多干货

概述

  • Kafka消息组织原理
  • Kafka消息检索原理

一、磁盘重认识

image

1、当需要从磁盘读取数据时,要确定读的数据在哪个磁道,哪个扇区:

  • 首先必须找到柱面,即磁头需要移动对准相应磁道,这个过程叫做寻道,所耗费时间叫做寻道时间;

  • 然后目标扇区旋转到磁头下,这个过程耗费的时间叫做旋转时间;

2、一次访盘请求(读/写)完成过程由三个动作组成

  • 寻道(时间):磁头移动定位到指定磁道;
  • 旋转延迟(时间):等待指定扇区从磁头下旋转经过;
  • 数据传输(时间):数据在磁盘、内存与网络之间的实际传输

由于存储介质的特性,磁盘本身存取就比主存慢,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分之一甚至几千分支一

3、怎么样才能提高磁盘的读写效率呢

根据数据的局部性原理 ,有以下两种方法

  • 预读或者提前读;

  • 合并写——多个逻辑上的写操作合并成一个大的物理写操作中;

即采用磁盘顺序读写(不需要寻道时间,只需很少的旋转时间)。实验结果:在一个6 7200rpm SATA RAID-5 的磁盘阵列上线性写的速度大概是300M/秒,但是随机写的速度只有50K/秒,两者相差将近10000倍。

二、Kafka消息的写入原理

image

Kafka topic信息

image

Kafka 消息文件存储

image

Kafka消息删除原理

从最久的日志段开始删除(按日志段为单位进行删除),然后逐步向前推进,直到某个日志段不满足条件为止,删除条件:

  • 满足给定条件predicate(配置项log.retention.{ms,minutes,hours}和log.retention.bytes指定);
  • 不能是当前激活日志段;
  • 大小不能小于日志段的最小大小(配置项log.segment.bytes配置)要删除的是否是所有日志段,如果是的话直接调用roll方法进行切分,因为Kafka至少要保留一个日志段;

image

recovery-point-offset-checkpoint:表示已经刷写到磁盘的记录

flush刷盘机制

熟悉Linux操作系统原理的都知道,当我们把数据写入到文件系统之后,数据其实在操作系统的page cache里面,并没有刷到磁盘上去。如果此时操作系统挂了,其实数据就丢了。

一方面,应用程序可以调用fsync这个系统调用来强制刷盘;另一方面,操作系统有后台线程,定期刷盘。

如果应用程序每写入1次数据,都调用一次fsync,那性能损耗就很大,所以一般都会在性能和可靠性之间进行权衡。因为对应一个应用来说,虽然应用挂了,只要操作系统不挂,数据就不会丢。

另外, kafka是多副本的,当你配置了同步复制之后。多个副本的数据都在page cache里面,出现多个副本同时挂掉的概率比1个副本挂掉,概率就小很多了。

对于kafka来说,也提供了相关的配置参数,来让你在性能与可靠性之间权衡:

log.flush.interval.messages  //多少条消息,刷盘1次
log.flush.interval.ms  //割多长时间,刷盘1次
log.flush.scheduler.interval.ms //周期性的刷盘,缺省3000,即3s。

默认前2个参数都没有设置,也就是只第3个参数起作用,即3s钟刷盘1次。

三、Kafka消息的segment file的组成和物理结构

image

image

image

image

image

Kafka消息检索过程

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

  • 第一步查找segment file;

以上图为例,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1。只要根据offset二分查找文件列表,就可以快速定位到具体文件。当offset=368776时定位到00000000000000368769.index|log

  • 第二步通过segment file查找message;

算出368776-368770=6,取00000000000000368769.index文件第三项(6,1407),得出从00000000000000368769.log文件头偏移1407字节读取一条消息即可

每个topic_partition对应一个目录

假设有一个topic叫my_topic,3个partition,分别为my_topic_0, my_topic_1, my_topic_2。其中前2个parition存在1台机器A,另外一个paritition存在另1台机器B上(这里A, B都是指对应partition的leader机器)。

则机器A上,就有2个对应的目录my_topic_0, my_topic_1,也就是每个目录对应一个topic_partition,目录名字就是topic_partition名字。

而目录中,日志文件按照大小或者时间回滚,文件名字为00000000.kafka, 1200000.kakfa。。。

文件名字是上1个log文件中,最后1条消息的offset。这里的1200000.kafka,就是说,在这个文件之前,有1200000条消息。

同时,Kafka中有一个配置参数

所有的topic_partition目录也就存放在这个里面。

文件offset作为Message ID

正如上面所说,kafka没有额外用类似uuid的方式,为每条消息生成一个唯一的message id,而是直接用该message在文件中的offset,作为该message的id。

这里有一个关键点:这里的offset并不是消息在文件中的物理位置,而是一个顺序递增的逻辑编号。从0开始,每append一条消息,offset加1。

index文件

上面说了,每个消息通过一个顺序递增的逻辑编号offset作为其message Id。但消息都是变长的,那怎么通过其offset找到消息所在的文件位置呢?

比如要找offset = 95的消息,最朴素的办法就是:按顺序扫描所有文件,从第0条记录开始读起,然后读到offset = 95 所在的消息位置。但这个办法效率太低。

Kafka引入了index文件。每1个.kakfa文件,都有一个相对应的.index文件,2者文件名字完全一样

具体根据offset查找文件位置,分为3步:

  • 第1步:把所有.kakfa的文件名字排序,通过2分查找,得到所在的.kakfa文件
  • 第2步:到对应的.index文件里面,再2分查找,找到对应的条目,也就是offset到position的映射。
  • 第3步:拿到这个position,就直接定位到.kakfa文件中相应的位置。然后从拿个位置开始,顺序扫描,就能得到实际所在position。

这里.index文件的目的,就是为了加快查找速度。即使没有这个文件,第1步拿到了.kakfa文件,你从头扫描,也能找到位置。

所以这个的.index存的是一个稀疏索引,每隔一个范围,存储1条消息的(offset, position)的对应关系。

变长消息存储

我们知道,kafka的消息是变长的。对于变长记录的存储,一般都是在记录的最前面,用固定的几个字节(比如4个字节),来存储记录的长度。

读的时候,先读取这固定的4个字节,获得记录长度,再根据长度读取后面内容。

多线程写同一个Log文件

前面说到,网络模型是1+N+M,也就意味着M个worker线程可能写同1个log文件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值