要深入学习Kafka,理解Kafka的存储机制是非常重要的。本文介绍Kafka存储消息的格式以及数据文件和索引组织方式,以便更好的理解Kafka是如何工作的。

Kafka消息存储格式

Kafka为了保证消息的可靠性,服务端会将接收的消息进行序列化并保存到磁盘上(Kafka的多副本存储机制),这里涉及到消息的存储格式,即消息编码后落到磁盘文件上的二进制的数据格式。下图是根据Kafka 3.0官方文档整理的消息格式:

包含三个部分:BatchRecords、Record,以及Header的编码格式。

BatchRecords是Kafka数据的存储单元,一个BatchRecords中包含多个Record(即我们通常说的一条消息)。BatchRecords中各个字段的含义如下:

字段名 含义
baseOffset 这批消息的起始Offset
partitionLeaderEpoch 用于Partition的Recover时保护数据的一致性,具体场景可以见KIP101
batchLength BatchRecords的长度
magic 魔数字段,可以用于拓展存储一些信息,当前3.0版本的magic是2
crc crc校验码,包含从attributes开始到BatchRecords结束的数据的校验码
attributes int16,其中bit0~2中包含了使用的压缩算法,bit3是timestampType,bit4表示是否失误,bit5表示是否是控制指令,bit6~15暂未使用
lastOffsetDelta BatchRecords中最后一个Offset,是相对baseOffset的值
firstTimestamp BatchRecords中最小的timestamp
maxTimestamp BatchRecords中最大的timestamp
producerId 发送端的唯一ID,用于做消息的幂等处理
producerEpoch 发送端的Epoch,用于做消息的幂等处理
baseSequence BatchRecords的序列号,用于做消息的幂等处理
records 具体的消息内容

一个BatchRecords中可以包含多条消息,即上图中的Record,而每条消息又可以包含多个Header信息,Header是Key-Value形式的。Record和Header的格式都非常简单,就不对其中的字段做解释了。

Log Segment

在Kafka中,一个Topic会被分割成多个Partition,而Partition由多个更小的,称作Segment的元素组成。

Kafka一个Partition下会包含类似上图中的一些文件,由log、index、timeindex三个文件组成一个Segment,而文件名中的(0和35)表示的是一个Segment的起始Offset(Kafka会根据log.segment.bytes的配置来决定单个Segment文件(log)的大小,当写入数据达到这个大小时就会创建新的Segment)。log、index、timeindex中存储的都是二进制的数据(log中存储的就是上一部分介绍的BatchRecords的内容,而index和timeindex分别是一些索引信息。)

下图是log文件中数据解析后的示意图(也就是本文第一部分BatchRecords格式)。其中16开头的这一行表示一个第一条消息的Offset是16的BatchRecord,而24开头的这一行表示的是一个第一条消息的Offset是24的BatchRecord。

索引

我们知道Kafka中每个Consumer消费一个Partition都会有一个关联的Offset表示已经处理过的消息的位置。通常Consumer会根据Offset连续的处理消息。而通过Offset从存储层中获取消息大致分为两步:

  • 第一步,根据Offset找到所属的Segment文件

  • 第二步,从Segment中获取对应Offset的消息数据

其中第一步可以直接根据Segment的文件名进行查找(上面已经介绍了Segment的文件面就是它包含的数据的起始Offset)。第二步则需要一些索引信息来快速定位目标数据在Segment中的位置,否则就要读取整个Segment文件了,这里需要的索引信息就是上面的index文件存储的内容。

index文件中存储的是Offset和Position(Offset对应的消息在log文件中的偏移量)的对应关系,这样当有Offset时可以快速定位到Position读取BatchRecord,然后再从BatchRecord中获取某一条消息。比如上述Offset25会被定位到24这个BatchRecord,然后再从这个BatchRecord中取出第二个Record(24这个BatchRecord包含了24、25两个Record)。

注意,Kafka并不会为每个Record都保存一个索引,而是根据log.index.interval.bytes等配置构建稀疏的索引信息。

除了index索引文件保存Offset和Position的映射关系外,Kafka中还维护了timeindex,保存了Timestamp和Offset的关系,用于应对一些场景需要根据timestamp来定位消息。timeindex中的一个(timestampX,offsetY)元素的含义是所有创建时间大于timestampX的消息的Offset都大于offsetY。

同样的,timeindex也采用了稀疏索引的机制,使用和index相同的配置(log.index.interval.bytes),所以timeindex和index是一一对应的。

总结

本文首先介绍了Kafka消息的存储格式,然后介绍了Kafka是如何索引(index & timeindex)存储的数据的。看完索引部分后遗留了一个疑问:每次读取消息都要先根据索引读取Position信息,然后再根据Position去读数据,而索引又是稀疏索引(查找索引也是要开销的),这样效率是否会比较低呢?如果利用Consumer总是顺序读取消息的特性,每次读取数据时都带上一些上下文信息(比如上一次Offset对应的Position信息)直接去读Log数据效率是否会更高?欢迎留言交流!

Kafka消息(存储)格式及索引组织方式的更多相关文章

  1. Kafka消息系统基础知识索引

    一些观念的修正 从 0.9 版本开始,Kafka 的标语已经从“一个高吞吐量,分布式的消息系统”改为"一个分布式流平台". Kafka不仅仅是一个队列,而且是一个存储,有超强的堆积 ...

  2. Kafka(3)--kafka消息的存储及Partition副本原理

    消息的存储原理: 消息的文件存储机制: 前面我们知道了一个 topic 的多个 partition 在物理磁盘上的保存路径,那么我们再来分析日志的存储方式.通过 [root@localhost ~]# ...

  3. Kafka消息文件存储

    在对消息进行存储和缓存时,Kafka依赖于文件系统.(Page Cache) 线性读取和写入是所有使用模式中最具可预计性的一种方式,因而操作系统采用预读(read-ahead)和后写(write-be ...

  4. 一文看懂Kafka消息格式的演变

    摘要 对于一个成熟的消息中间件而言,消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化.随着Kafka的迅猛发展,其消息格式也在不断的升级改进,从0.8.x版本开始到现在的1.1.x版本,Kaf ...

  5. 转载来自朱小厮博客的 一文看懂Kafka消息格式的演变

    转载来自朱小厮博客的 一文看懂Kafka消息格式的演变     ✎摘要 对于一个成熟的消息中间件而言,消息格式不仅关系到功能维度的扩展,还牵涉到性能维度的优化.随着Kafka的迅猛发展,其消息格式也在 ...

  6. Kafka分片存储、消息分发和持久化机制

    Kafka 分片存储机制 Broker:消息中间件处理结点,一个 Kafka 节点就是一个 broker,多个 broker 可以组成一个 Kafka集群. Topic:一类消息,例如 page vi ...

  7. MyISAM的前缀压缩索引在索引块中的组织方式

    纯粹自己的理解,哪位大佬看到了还请指正. 首先贴一张<高性能MySQL>中的一段话: 这句话的意思是说,MyISAM使用b+树组织索引.也就是说无论索引压缩与否,组织方式一定是B+树. 下 ...

  8. 联合索引在B+树上的存储结构及数据查找方式

    能坚持别人不能坚持的,才能拥有别人未曾拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! 引言 上一篇文章<MySQL索引那些事>主要讲了MySQL索引的底层原理,且对比了B ...

  9. Kafka日志存储原理

    引言 Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相互独立的.每个topic又可以分成几个不同的partition(每个topic有几个partition是在创建 ...

随机推荐

  1. Python习题集(十)

    每天一习题,提升Python不是问题!!有更简洁的写法请评论告知我! https://www.cnblogs.com/poloyy/category/1676599.html 题目 使用列表生成式语法 ...

  2. 如何实现自定义sk_buff数据包并提交协议栈

    目录 一.自定义数据包的封装流程 1. 分配skb 2.初始定位(skb_reserve) 3.拷贝数据(skb_push / skb_pull / skb_put / ) 4.设置传输层头部 5.设 ...

  3. 这篇 Java 基础,我吹不动了

    Hey guys,这里是程序员cxuan,欢迎你收看我最新一期的文章,这篇文章我补充了一些关于<Java基础核心总结>的内容,修改了部分错别字和语句不通顺的地方,并且对内部类.泛型等内容进 ...

  4. Nacos注册中心之概要设计

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 前言 在之前的文章中分析了Nacos配置中心,配置中心的核心是配置的创建.读取.推送. 注册中 ...

  5. [Apache Atlas] Atlas 架构设计及源代码简单分析

    Apache Atlas 架构图 Atlas 支持多数据源接入:Hive.HBase.Storm等 Type System Type Atlas 中定义了一些元数据类型 ── AtlasBaseTyp ...

  6. ecshop调用指定分类热销-新品-精品

    在模板页里首页写上代码:         <?php$children = get_children(16);//此处为产品分类ID$smarty->assign( 'bestGoods1 ...

  7. Dapr + .NET Core实战(四)发布和订阅

    什么是发布-订阅 发布订阅是一种众所周知并被广泛使用的消息传送模式,常用在微服务架构的服务间通信,高并发削峰等情况.但是不同的消息中间件之间存在细微的差异,项目使用不同的产品需要实现不同的实现类,虽然 ...

  8. 再谈OAuth授权

    工作场景流程 大家都知道OAuth是用于第三方授权的,当你用其他的APP想访问微信账号的昵称.粉丝.联系人等信息,这里就需要微信进行授权,此时在APP的网页端是可以发现有微信登录的,点开会出现弹框,在 ...

  9. spring入门3-jdbcTemplate简单使用和声明式事务

    1.JdbcTemplate简单使用 1.1.引入相关依赖包 <dependency> <groupId>mysql</groupId> <artifactI ...

  10. Microfacet模型采样下的brdf

    本文前言 在学习图形学(games101 from bilibili)的时候,也遇到了像这样的问题,Cook-Torrance模型无法实现粗糙度为0时,物体微表面呈现绝对镜面的效果(呈现出一面镜子), ...