kafka中的消息,是以主题进行归类的,每个主题分为一个或多个分区,主题和分区是逻辑上的概念。消息在发送时,会按照规则追加到其中一个分区中。
分区里的每一条消息,都会被分配一个唯一的序列号,也就是偏移量(offset)
分区是逻辑上的概念,往分区追加消息时,其实是写到日志(Log)中,为了防止日志过大,kafka还有日志分段(LogSegment)的概念,Log在物理上是以文件夹的形式存储,每个LogSegment对应于磁盘上的一个日志文件和两个索引文件,以及其他文件。

Java kafka偏移量和数据条数 kafka偏移量保存在哪里_Kafka

kafka追加消息是顺序写,由于Log被分成了多个LogSegment,所以只有最后一个LogSegment才能执行写入操作,之前的LogSegment不能写入数据。
为了便于消息的检索,每个.log文件都有对应的两个索引文件:偏移量文件和时间戳索引文件。
偏移量索引文件用来建立消息偏移量到物理地址之间的映射关系,方便快速定位消息所在的物理文件位置,时间戳索引文件则根据指定的时间戳来查找对应的偏移量信息。

kafka存储和缓存消息使用的是磁盘,但是按照我们之前的认知,磁盘的速度并不快,远低于内存,缓存这种存储介质,那kafka为什么会选择用磁盘作为存储介质呢?

Java kafka偏移量和数据条数 kafka偏移量保存在哪里_数据_02

这个和kafka的消息机制有关系,上面提到过,kafka的消息是顺序写的方式,只能在日志文件的尾部追加新的消息,并且不允许修改已经写入的消息。操作系统可以针对这种方式进行优化,比如预读和后写,从而达到较快的写入速度。

Java kafka偏移量和数据条数 kafka偏移量保存在哪里_kafka_03


按照6块7200r/min的RAID-5阵列组成的磁盘簇进行试验,能够看出,顺序写磁盘的速度,比随机写内存的速度还要快。所以,kafka的这种设计,完全可以采用磁盘作为存储介质。

除此之外,kafka还有其他很多机制来提升性能,比如:页缓存、零拷贝。

页缓存:
页缓存是操作系统实现的一种磁盘缓存,把磁盘中的数据缓存到内存中,从而减少对磁盘的I/O操作。通过这种方式,把对磁盘的访问变成对内存的访问。
当一个进程准备读取磁盘上的内容时,操作系统会先查看待读取的数据所在的页是否在页缓存中,如果在,则直接返回数据,不需要进行磁盘I/O,如果不在页缓存,则操作系统会向磁盘发起读取请求,并将读取到的内容缓存到页缓存中,之后再将数据返回给进程。同样,如果进程需要写数据到磁盘,操作系统也会先检测数据对应的页是否在页缓存中,如果不存在,则会先在页缓存中中添加相应的页,最后再将数据写入页缓存,被修改的页变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的一致性。
kafka中大量使用了页缓存,消息先被写入页缓存,然后再由操作系统异步刷盘。并且kafka也提供了同步刷盘以及间断性强制刷盘的功能。
为什么利用页缓存可以提升性能呢?这里就要从进程内的缓存和页缓存来进行分析了。对于一个进程而言(比如kafka broker进程),可以在进程内部缓存处理所需的数据,然而这些数据还有可能缓存在操作系统页缓存中,因此同一份数据可能被缓存了两次。而且,进程内的缓存,开销非常大,通常是真实数据的几倍甚至更多,这样使得内存空间利用率很低,java的垃圾回收也会随着堆内数据的增多而变得越来越慢,所以,采用页缓存的方式会比进程内缓存要好,可以节省内存空间,把机器的大部分内存用作页缓存,也不需要担心GC的问题,即使Kafka进程重启,页缓存也依然保持有效,可以简化代码的逻辑。

零拷贝:

零拷贝是指将数据直接从磁盘文件复制到网卡设备,不需要经过应用程序,减少了内核和用户模式之间的上下文切换,大大提高了应用程序的性能。

举个例子,假如现在需要将一个图片展示给用户,那服务器的处理流程是这样的,先把文件从磁盘中复制出来,放到内存buf中,然后将这个buf通过套接字(Socket)传输给用户。其实这个流程的效率很低,因为这个过程,图片经历了4次复制过程:

Java kafka偏移量和数据条数 kafka偏移量保存在哪里_缓存_04


(1)图片复制到内核模式下的Read Buffer。

(2)CPU将内核模式数据复制到用户模式下。

(3)将用户模式下的内容复制到内核模式下的Socket Buffer。

(4)将内核模式下的Socket Buffer的数据复制到网卡设备中传送。

这种普通的方式,数据额外被复制了两次,第一次是从内核模式复制到用户模式,对应上面第二步;第二次是从用户模式复制回内核模式。对应上面第三步。如果采用了零拷贝,那应用程序可以直接请求内核把磁盘的数据传输给Socket,流程如下:

Java kafka偏移量和数据条数 kafka偏移量保存在哪里_缓存_05


零拷贝技术通过DMA技术将文件内容复制到内核模式下的Read Buffer中,不过只有包含数据的位置和长度信息的文件描述符被加到Socket Buffer中,文件内容没有被复制到Socket Buffer。DMA引擎直接将数据从内核模式中传递到网卡设备,这里数据只经历了2次复制就从磁盘中传送出去了。零拷贝是针对内核模式而言的,数据在内核模式下实现了零拷贝。