kafka存储机制

(一)关键术语

1.Broker:消息中间件处理结点,一个Kafka节点就是一个broker,多个broker能够组成一个Kafka集群。

2.Topic:一类消息,比如page view日志、click日志等都能够以topic的形式存在。Kafka集群能够同一时候负责多个topic的分发。

3.Partition:topic物理上的分组。一个topic能够分为多个partition,每一个partition是一个有序的队列。

4.Segment:partition物理上由多个segment组成。以下有具体说明。

5.offset:每一个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每一个消息都有一个连续的序列号叫做offset,用于partition中唯一标识的这条消息。

二、Kafka文件存储基本结构

在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个分区,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。
每个partion(分区)相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件中。但每个段segment file消息数量不一定相等,这种特性方便old segment file快速被删除。默认保留7天的数据。

每个partiton只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定。(什么时候创建,什么时候删除)## 分析过程分为以下4个步骤:
在这里插入图片描述
数据有序的讨论?
一个partition的数据是否是有序的? 间隔性有序,不连续
针对一个topic里面的数据,只能做到partition内部有序,不能做到全局有序。
特别加入消费者的场景后,如何保证消费者消费的数据全局有序的?伪命题。

只有一种情况下才能保证全局有序?就是只有一个partition。

分析过程分为以下4个步骤

1.topic中partition存储分布
2.partiton中文件存储方式
3.partiton中segment文件存储结构
4.在partition中怎样通过offset查找mess

1.topic中partition存储分布

如果实验环境中Kafka集群仅仅有一个broker。xxx/message-folder为数据文件存储根文件夹。在Kafka broker中server.properties文件配置(參数log.dirs=xxx/message-folder)。比如创建2个topic名称分别为report_push、launch_info, partitions数量都为partitions=4
存储路径和文件夹规则为:
xxx/message-folder
|–report_push-0
|–report_push-1
|–report_push-2
|–report_push-3
|–launch_info-0
|–launch_info-1
|–launch_info-2
|–launch_info-3
在Kafka文件存储中,同一个topic下有多个不同partition,每一个partition为一个文件夹,partiton命名规则为topic名称+有序序号,第一个partiton序号从0開始,序号最大值为partitions数量减1。

如果是多broker分布情况,请參考kafka集群partition分布原理分析

2.partiton中文件存储方式

以下示意图形象说明了partition中文件存储方式:
在这里插入图片描述
每一个partion(文件夹)相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件里。

但每一个段segment file消息数量不一定相等,这样的特性方便old segment file高速被删除。(默认情况下每一个文件大小为1G)

每一个partiton仅仅须要支持顺序读写即可了。segment文件生命周期由服务端配置參数决定。

这样做的优点就是能高速删除无用文件。有效提高磁盘利用率。

3.partiton中segment文件存储结构

segment file组成:由2大部分组成。分别为index file和data file,此2个文件一一相应,成对出现,后缀”.index”和“.log”分别表示为segment索引文件、数据文件.

segment文件命名规则:partion全局的第一个segment从0開始,兴许每一个segment文件名称为上一个segment文件最后一条消息的offset值。

数值最大为64位long大小。19位数字字符长度,没有数字用0填充。

以下文件列表是笔者在Kafka broker上做的一个实验,创建一个topicXXX包括1 partition,设置每一个segment大小为500MB,并启动producer向Kafka broker写入大量数据,例如以下图2所看到的segment文件列表形象说明了上述2个规则:
在这里插入图片描述
以上述图2中一对segment file文件为例。说明segment中index<—->data file相应关系物理结构例如以下:
在这里插入图片描述
上述图3中索引文件存储大量元数据,数据文件存储大量消息,索引文件里元数据指向相应数据文件里message的物理偏移地址。

当中以索引文件里元数据3,497为例,依次在数据文件里表示第3个message(在全局partiton表示第368772个message)、以及该消息的物理偏移地址为497。

从上述图3了解到segment data file由很多message组成,以下具体说明message物理结构例如以下:
在这里插入图片描述

4.keyword 解释说明

8 byte offset 在parition(分区)内的每条消息都有一个有序的id号,这个id号被称为偏移(offset),它能够唯一确定每条消息在parition(分区)内的位置。

即offset表示partiion的第多少message
4 byte message size message大小
4 byte CRC32 用crc32校验message
1 byte “magic” 表示本次公布Kafka服务程序协议版本号号
1 byte “attributes” 表示为独立版本号、或标识压缩类型、或编码类型。

4 byte key length 表示key的长度,当key为-1时,K byte key字段不填
K byte key 可选
value bytes payload 表示实际消息数据。

5.在partition中怎样通过offset查找message

比如读取offset=368776的message,须要通过以下2个步骤查找。
第一步查找segment file
上述图2为例。当中00000000000000000000.index表示最開始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.相同,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1。其它兴许文件依次类推。以起始偏移量命名并排序这些文件,仅仅要依据offset 二分查找文件列表,就能够高速定位到具体文件。
当offset=368776时定位到00000000000000368769.index|log

第二步通过segment file查找message
通过第一步定位到segment file,当offset=368776时。依次定位到00000000000000368769.index的元数据物理位置和00000000000000368769.log的物理偏移地址,然后再通过00000000000000368769.log顺序查找直到offset=368776为止。

从上述图3可知这样做的优点,segment index file採取稀疏索引存储方式,它降低索引文件大小。通过mmap能够直接内存操作,稀疏索引为数据文件的每一个相应message设置一个元数据指针,它比稠密索引节省了很多其它的存储空间,但查找起来须要消耗很多其它的时间。

6.Kafka文件存储机制–实际执行效果

实验环境:

Kafka集群:由2台虚拟机组成

cpu:4核

物理内存:8GB

网卡:千兆网卡

jvm heap: 4GB

具体Kafka服务端配置及其优化请參考:kafka server.properties配置具体解释
在这里插入图片描述

从上述图5能够看出,Kafka执行时非常少有大量读磁盘的操作。主要是定期批量写磁盘操作。因此操作磁盘非常高效。

这跟Kafka文件存储中读写message的设计是息息相关的。Kafka中读写message有例如以下特点:
写message
消息从java堆转入page cache(即物理内存)。
由异步线程刷盘,消息从page cache刷入磁盘。
读message
消息直接从page cache转入socket发送出去。
当从page cache没有找到相应数据时,此时会产生磁盘IO,从磁
盘Load消息到page cache,然后直接从socket发出去

7.总结

Kafka高效文件存储设计特点
1.Kafka把topic中一个parition大文件分成多个小文件段。通过多个小文件段,就easy定期清除或删除已经消费完文件。降低磁盘占用。

2.通过索引信息能够高速定位message和确定response的最大大小。

3.通过index元数据所有映射到memory,能够避免segment file的IO磁盘操作。

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

转https://www.cnblogs.com/cynchanpin/p/7339537.html

kafka为什么快

不同于Redis和MemcacheQ等内存消息队列,Kafka的设计是把所有的Message都要写入速度低容量大的硬盘,以此来换取更强的存储能力。实际上,Kafka使用硬盘并没有带来过多的性能损失,“规规矩矩”的抄了一条“近道”。

首先,说“规规矩矩”是因为Kafka在磁盘上只做Sequence I/O,由于消息系统读写的特殊性,这并不存在什么问题。关于磁盘I/O的性能,引用一组Kafka官方给出的测试数据(Raid-5,7200rpm):

Sequence I/O: 600MB/s
Random I/O: 100KB/s

所以通过只做Sequence I/O的限制,规避了磁盘访问速度低下对性能可能造成的影响。

接下来我们再聊一聊Kafka是如何“抄近道的”。

首先,Kafka重度依赖底层操作系统提供的PageCache功能。当上层有写操作时,操作系统只是将数据写入PageCache,同时标记Page属性为Dirty。

**当读操作发生时,先从PageCache中查找,如果发生缺页才进行磁盘调度,最终返回需要的数据。**实际上PageCache是把尽可能多的空闲内存都当做了磁盘缓存来使用。同时如果有其他进程申请内存,回收PageCache的代价又很小,所以现代的OS都支持PageCache。

使用PageCache功能同时可以避免在JVM内部缓存数据,JVM为我们提供了强大的GC能力,同时也引入了一些问题不适用与Kafka的设计。
如果在Heap内管理缓存,JVM的GC线程会频繁扫描Heap空间,带来不必要的开销。如果Heap过大,执行一次Full GC对系统的可用性来说将是极大的挑战。
所有在在JVM内的对象都不免带有一个Object Overhead(千万不可小视),内存的有效空间利用率会因此降低。
所有的In-Process Cache在OS中都有一份同样的PageCache。所以通过将缓存只放在PageCache,可以至少让可用缓存空间翻倍。
如果Kafka重启,所有的In-Process Cache都会失效,而OS管理的PageCache依然可以继续使用。

PageCache还只是第一步,Kafka为了进一步的优化性能还采用了Sendfile技术。在解释Sendfile之前,首先介绍一下传统的网络I/O操作流程,大体上分为以下4步。

OS 从硬盘把数据读到内核区的PageCache。
用户进程把数据从内核区Copy到用户区。
然后用户进程再把数据写入到Socket,数据流入内核区的Socket Buffer上。
OS 再把数据从Buffer中Copy到网卡的Buffer上,这样完成一次发送。
在这里插入图片描述
整个过程共经历两次Context Switch,四次System Call。同一份数据在内核Buffer与用户Buffer之间重复拷贝,效率低下。其中2、3两步没有必要,完全可以直接在内核区完成数据拷贝。这也正是Sendfile所解决的问题,经过Sendfile优化后,整个I/O过程就变成了下面这个样子。

猜你喜欢

转载自blog.csdn.net/qq_35078688/article/details/84993815