摘自http://blog.csdn.net/jewes/article/details/42970799

引言

Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相互独立的。每个topic又可以分成几个不同的partition(每个topic有几个partition是在创建topic时指定的),每个partition存储一部分Message。借用官方的一张图,可以直观地看到topic和partition的关系。

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的数据。

接下来,本文将分析partition目录中的文件的存储格式和相关的代码所在的位置。

Partition的数据文件

Partition中的每条Message由offset来表示它在这个partition中的偏移量,这个offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了partition中的一条Message。因此,可以认为offset是partition中Message的id。partition中的每条Message包含了以下三个属性:

  • offset
  • MessageSize
  • data

其中offset为long型,MessageSize为int32,表示data有多大,data为message的具体内容。它的格式和Kafka通讯协议中介绍的MessageSet格式是一致。

Partition的数据文件则包含了若干条上述格式的Message,按offset由小到大排列在一起。它的实现类为FileMessageSet,类图如下:

它的主要方法如下:

  • append: 把给定的ByteBufferMessageSet中的Message写入到这个数据文件中。
  • searchFor: 从指定的startingPosition开始搜索找到第一个Message其offset是大于或者等于指定的offset,并返回其在文件中的位置Position。它的实现方式是从startingPosition开始读取12个字节,分别是当前MessageSet的offset和size。如果当前offset小于指定的offset,那么将position向后移动LogOverHead+MessageSize(其中LogOverHead为offset+messagesize,为12个字节)。
  • read:准确名字应该是slice,它截取其中一部分返回一个新的FileMessageSet。它不保证截取的位置数据的完整性。
  • sizeInBytes: 表示这个FileMessageSet占有了多少字节的空间。
  • truncateTo: 把这个文件截断,这个方法不保证截断位置的Message的完整性。
  • readInto: 从指定的相对位置开始把文件的内容读取到对应的ByteBuffer中。

我们来思考一下,如果一个partition只有一个数据文件会怎么样?

  1. 新数据是添加在文件末尾(调用FileMessageSet的append方法),不论文件数据文件有多大,这个操作永远都是O(1)的。
  2. 查找某个offset的Message(调用FileMessageSet的searchFor方法)是顺序查找的。因此,如果数据文件很大的话,查找的效率就低。

那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。

  • 相对offset:因为数据文件分段以后,每个数据文件的起始offset不为0,相对offset表示这条Message相对于其所属数据文件中最小的offset的大小。举例,分段后的一个数据文件的offset是从20开始,那么offset为25的Message在index文件中的相对offset就是25-20 = 5。存储相对offset可以减小索引文件占用的空间。
  • position,表示该条Message在数据文件中的绝对位置。只要打开文件并移动文件指针到这个position就可以读取对应的Message了。

index文件中并没有为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。

在Kafka中,索引文件的实现类为OffsetIndex,它的类图如下:

主要的方法有:

  • append方法,添加一对offset和position到index文件中,这里的offset将会被转成相对的offset。
  • lookup, 用二分查找的方式去查找小于或等于给定offset的最大的那个offset

小结

我们以几张图来总结一下Message是如何在Kafka中存储的,以及如何查找指定offset的Message的。

Message是按照topic来组织,每个topic可以分成多个的partition,比如:有5个partition的名为为page_visits的topic的目录结构为:

partition是分段的,每个段叫LogSegment,包括了一个数据文件和一个索引文件,下图是某个partition目录下的文件:

可以看到,这个partition有4个LogSegment。

借用博主@lizhitao博客上的一张图来展示是如何查找Message的。

比如:要查找绝对offset为7的Message:

  1. 首先是用二分查找确定它是在哪个LogSegment中,自然是在第一个Segment中。
  2. 打开这个Segment的index文件,也是用二分查找找到offset小于或者等于指定offset的索引条目中最大的那个offset。自然offset为6的那个索引是我们要找的,通过索引文件我们知道offset为6的Message在数据文件中的位置为9807。
  3. 打开数据文件,从位置为9807的那个地方开始顺序扫描直到找到offset为7的那条Message。

这套机制是建立在offset是有序的。索引文件被映射到内存中,所以查找的速度还是很快的。

一句话,Kafka的Message存储采用了分区(partition),分段(LogSegment)和稀疏索引这几个手段来达到了高效性。

转载于:https://www.cnblogs.com/chen-kh/p/6095524.html

Kafka深入理解-2:Kafka的Log存储解析相关推荐

  1. 【kafka原理】kafka Log存储解析以及索引机制

    本文设置到的配置项有 名称 描述 类型 默认 num.partitions topic的默认分区数 int 1 log.dirs 保存日志数据的目录.如果未设置,则使用log.dir中的值 strin ...

  2. 大数据技术之 Kafka (第 3 章 Kafka 架构深入 ) Log存储解析

    Kafka 工作流程 Kafka 中消息是以 topic 进行分类的,生产者生产消息,消费者消费消息,都是面向 topic的.  topic 是逻辑上的概念,而 partition 是物理上的概念,每 ...

  3. 深入浅出理解基于 Kafka 和 ZooKeeper 的分布式消息队列

    https://gitbook.cn/books/5ae1e77197c22f130e67ec4e/index.html 向作者提问 毕业于 C9 高校,硕士学历,曾在 IEEE ITS.VSD 等 ...

  4. Kafka Log Compaction 解析

    最近查看Kafka文档, 发现 Kafka 有个 Log Compaction 功能是我们之前没有留意到的, 但是有着很高的潜在实用价值. 什么是Log Compaction Kafka 中的每一条数 ...

  5. kafka日志对象(一)—— Log Segment

    kafka的高吞吐量和持久性是一大亮点,内部的日志操作是如何呢,研究明白了一定豁然开朗,至少我有种恍然大明白的感觉. kafka的日志结构 Kafka 日志对象由多个日志段对象组成,而每个日志段对象会 ...

  6. kafka集群中常见错误的解决方法:kafka.common.KafkaException: Should not set log end offset on partition

    kafka集群中常见错误的解决方法:kafka.common.KafkaException: Should not set log end offset on partition 参考文章: (1)k ...

  7. 高效实用Kafka-深入理解Kafka启动配置(使用kafka自身内置Zookeeper)

    导语   在上一篇博客中简单的介绍了关于消息系统消息中间件技术以及Kafka的一些基础.这篇文章主要是来介绍关于Kafka的架构以及如何安装Kafka.  首先先来安装Kafka,对于Kafka的安装 ...

  8. Kafka内核理解:消息的收集/消费机制

    一.Kafka数据收集机制 Kafka集群中由producer负责数据的产生,并发送到对应的Topic:Producer通过push的方式将数据发送到对应Topic的分区 Producer发送到Top ...

  9. kafka 基础知识梳理-kafka是一种高吞吐量的分布式发布订阅消息系统

    一.kafka 简介 今社会各种应用系统诸如商业.社交.搜索.浏览等像信息工厂一样不断的生产出各种信息,在大数据时代,我们面临如下几个挑战: 如何收集这些巨大的信息 如何分析它 如何及时做到如上两点 ...

最新文章

  1. R语言ggplot2可视化:可视化多个图像并组合起来(如果没有对齐,看着丑陋))、保证组合的图像图像区域对齐或者图像整体对齐(Align plot areas in ggplot)
  2. Tungsten Fabric SDN — 流量调试手段
  3. 安装memcached服务 和 php 安装memcache扩展
  4. vue从入门到进阶:自定义指令directive,插件的封装以及混合mixins(七)
  5. vue-resource jsonp跨域问题解决方法
  6. mysql 载入主体时出错_mysql遇到load data导入文件数据出现1290错误的解决方案
  7. 【Core Swagger】.NET Core中使用swagger
  8. ubuntu火狐证书问题
  9. 手机展示海报PSD模板、适用众多设计!
  10. 《Docker技术入门与实战》——3.5 创建镜像
  11. 如何快速把英语单词导入有道词典
  12. 陈丽华(帮别人名字作诗)
  13. ICQ官方中文版 v10.0.12161.0
  14. 试图运行项目时出错:未能加载文件或程序集 或它的某一个依赖项。该模块应包含一个程序集清单 .
  15. 真人快打11 技能键位
  16. 年轻人说“接受自己的平庸”,八成是自欺欺人
  17. 不完全性定理 元数学和自然数_开读哥德尔原著第一章——哥德尔读后之十
  18. 华中科技大学计算机复试答案,华中科技大学计算机复试题目
  19. php斯芬克斯,斯芬克斯猫
  20. 谈一款MOBA类游戏《码神联盟》的服务端架构设计与实现 (转载)

热门文章

  1. 不需要任何依赖的图片加载错误处理的工具类load-image.js
  2. 从头开始学习vue-router
  3. jQuery中ajax的4种常用请求方式
  4. Windows Phone 8初学者开发—第12部分:改进视图模型和示例数据
  5. vs增量链接、清单文件
  6. CentOS 6.3编译安装Nginx1.2.2+MySQL5.5.25a+PHP5.4.5
  7. 利用程序动态管理Web.config文件的配置
  8. php的缓冲器,php输出缓存控制
  9. bootstrap table php,bootstrap table Tooltip
  10. 让你眼花缭乱的JS代码~~