原文 https://www.cnblogs.com/dorothychai/p/6181058.htmlhtml
Kafka中的Message是以topic为基本单位组织的,不一样的topic之间是相互独立的。每一个topic又能够分红几个不一样的partition(每一个topic有几个partition是在建立topic时指定的),每一个partition存储一部分Message。借用官方的一张图,能够直观地看到topic和partition的关系。app
partition是以文件的形式存储在文件系统中,好比,建立了一个名为page_visits的topic,其有5个partition,那么在Kafka的数据目录中(由配置文件中的log.dirs指定的)中就有这样5个目录: page_visits-0, page_visits-1,page_visits-2,page_visits-3,page_visits-4,其命名规则为<topic_name>-<partition_id>,里面存储的分别就是这5个partition的数据。atom
接下来,本文将分析partition目录中的文件的存储格式和相关的代码所在的位置。spa
Partition中的每条Message由offset来表示它在这个partition中的偏移量,这个offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,它惟一肯定了partition中的一条Message。所以,能够认为offset是partition中Message的id。partition中的每条Message包含了如下三个属性:.net
其中offset为long型,MessageSize为int32,表示data有多大,data为message的具体内容。它的格式和Kafka通信协议中介绍的MessageSet格式是一致。指针
Partition的数据文件则包含了若干条上述格式的Message,按offset由小到大排列在一块儿。它的实现类为FileMessageSet,类图以下:
它的主要方法以下:htm
咱们来思考一下,若是一个partition只有一个数据文件会怎么样?blog
那Kafka是如何解决查找效率的的问题呢?有两大法宝:1) 分段 2) 索引。索引
Kafka解决查询效率的手段之一是将数据文件分段,好比有100条Message,它们的offset是从0到99。假设将数据文件分红5段,第一段为0-19,第二段为20-39,以此类推,每段放在一个单独的数据文件里面,数据文件以该段中最小的offset命名。这样在查找指定offset的Message的时候,用二分查找就能够定位到该Message在哪一个段中。内存
数据文件分段使得能够在一个较小的数据文件中查找对应offset的Message了,可是这依然须要顺序扫描才能找到对应offset的Message。为了进一步提升查找的效率,Kafka为每一个分段后的数据文件创建了索引文件,文件名与数据文件的名字是同样的,只是文件扩展名为.index。
索引文件中包含若干个索引条目,每一个条目表示数据文件中一条Message的索引。索引包含两个部分(均为4个字节的数字),分别为相对offset和position。
index文件中并无为数据文件中的每条Message创建索引,而是采用了稀疏存储的方式,每隔必定字节的数据创建一条索引。这样避免了索引文件占用过多的空间,从而能够将索引文件保留在内存中。但缺点是没有创建索引的Message也不能一次定位到其在数据文件的位置,从而须要作一次顺序扫描,可是此次顺序扫描的范围就很小了。
在Kafka中,索引文件的实现类为OffsetIndex,它的类图以下:
主要的方法有:
咱们以几张图来总结一下Message是如何在Kafka中存储的,以及如何查找指定offset的Message的。
Message是按照topic来组织,每一个topic能够分红多个的partition,好比:有5个partition的名为为page_visits的topic的目录结构为:
partition是分段的,每一个段叫LogSegment,包括了一个数据文件和一个索引文件,下图是某个partition目录下的文件:
能够看到,这个partition有4个LogSegment。
借用博主@lizhitao博客上的一张图来展现是如何查找Message的。
好比:要查找绝对offset为7的Message:
这套机制是创建在offset是有序的。索引文件被映射到内存中,因此查找的速度仍是很快的。
一句话,Kafka的Message存储采用了分区(partition),分段(LogSegment)和稀疏索引这几个手段来达到了高效性。