文章目录

  • Kafka工作流程
  • 文件存储机制
  • 索引机制
  • 如何通过offset找到对应的消息?


Kafka工作流程

kafka消费者报错offset越界 kafka消费者的offset怎么存储_偏移量


Kafka 中消息是以topic进行分类的,生产者生产消息,消费者消费消息,都是面向topic的。

topic是逻辑上的概念,而partition是物理上的概念,每个partition对应于多个 log 文件,该 log 文件中存储的就是producer生产的数据。Producer生产的数据会被不断追加到该 log 文件末端,且每条数据都有自己的 offset。消费者组中的每个消费者,都会实时记录自己消费到了哪个offset,以便出错恢复时,从上次的位置继续消费。

文件存储机制

kafka消费者报错offset越界 kafka消费者的offset怎么存储_大数据_02


由于生产者生产的消息会不断追加到 log 文件末尾,为防止log文件过大导致数据定位效率低下,Kafka采取了分段索引机制,

partition 在服务器上的表现形式就是一个一个的文件夹(命名规则:topic名称+分区序号),每个 partition 文件夹下面会有多组 segment(逻辑分组,并不是真实存在),每个 segment 对应三个文件 (.log文件、.index文件、.timeindex文件)

log文件就实际是存储 message 的地方,而 index 和 timeindex 文件为索引文件,用于检索消息。

kafka消费者报错offset越界 kafka消费者的offset怎么存储_kafka消费者报错offset越界_03


如上图,这个 partition 有三组 segment 文件,每个 log 文件的大小是一样的,但是存储的 message 数量是不一定相等的(每条的 message 大小不一致)。文件的命名是以该 segment 最小 offset 来命名的,如 000.index 存储offset为0~368795的消息,kafka就是利用分段+索引的方式来解决查找效率的问题。

Message结构
log 文件就是实际存储 message 的地方,在 producer 往 kafka 写入的也是一条一条的 message,消息主要包含消息体、消息大小、offset、压缩类型…等!主要是下面三个:
  1、 offset:offset是一个占8byte的有序id号,它可以唯一确定每条消息在parition内的位置。
  2、 消息大小:消息大小占用4byte,用于描述消息的大小。
  3、 消息体:消息体存放的是实际的消息数据(被压缩过),占用的空间根据具体的消息而不一样。

存储策略
  无论消息是否被消费,kafka都会保存所有的消息。那对于旧数据有什么删除策略?
  1、 基于时间,默认配置是168小时(7天)。
  2、 基于大小,默认配置是1073741824。

#server.properties

#segment文件的大小,默认为 1G
log.segment.bytes=1024*1024*1024
#滚动生成新的segment文件的最大时长
log.roll.hours=24*7
#segment文件保留的最大时长,超时将被删除
log.retention.hours=24*7

索引机制

在kafka中,每个日志分段文件都对应了两个索引文件——.inde偏移量索引文件.timeindex时间戳索引文件,主要用来提高查找消息的效率。

偏移量索引文件:用来建立消息偏移量(offset)到物理地址之间的映射关系,方便快速定位消息所在的物理文件位置。
时间戳索引文件:则根据指定的时间戳(timestamp)来查找对应的偏移量信息。

Kafka 中的索引文件,以稀疏索引(sparse index)的方式构造消息的索引,它并不保证每个消息在索引文件中都有对应的索引项。每当写入一定量(由 broker 端参数 log.index.interval.bytes 指定,默认值为 4096,即 4KB)的消息时,偏移量索引文件 和 时间戳索引文件 分别增加一个偏移量索引项和时间戳索引项,增大或减小 log.index.interval.bytes 的值,对应地可以缩小或增加索引项的密度。

稀疏索引通过 MappedByteBuffer 将索引文件映射到内存中,以加快索引的查询速度。

偏移量索引文件中的偏移量是单调递增的,查询指定偏移量时,使用二分查找法来快速定位偏移量的位置,如果指定的偏移量不在索引文件中,则会返回小于指定偏移量的最大偏移量。

时间戳索引文件中的时间戳也保持严格的单调递增,查询指定时间戳时,也根据二分查找法来查找不大于该时间戳的最大偏移量,至于要找到对应的物理文件位置还需要根据偏移量索引文件来进行再次定位。

稀疏索引的方式是在磁盘空间、内存空间、查找时间等多方面之间的一个折中。

以偏移量索引文件来做具体分析。偏移量索引项的格式如下图所示。

每个索引项占用 8 个字节,分为两个部分:
(1) relativeOffset: 相对偏移量,表示消息相对于 baseOffset 的偏移量,占用 4 个字节(relativeOffset = offset - baseOffset),当前索引文件的文件名即为 baseOffset 的值。

  • 例如:一个日志片段的 baseOffset 为 32,那么其文件名就是 00000000000000000032.log,offset=35 的消息在索引文件中的 relativeOffset 的值为 35-32=3

(2) position: 物理地址,也就是消息在日志分段文件中对应的物理位置,占用 4 个字节。

kafka消费者报错offset越界 kafka消费者的offset怎么存储_kafka消费者报错offset越界_04


kafka消费者报错offset越界 kafka消费者的offset怎么存储_大数据_05

如何通过offset找到对应的消息?

kafka消费者报错offset越界 kafka消费者的offset怎么存储_kafka消费者报错offset越界_06


 

1、 先找到 offset=3 的 message 所在的 segment文件(利用二分法查找),先判断.index文件名称offset(baseOffset )是否小于3;

  • 若小于,则继续二分与下一个.inde文件名称offset比较;
  • 若大于,则返回上次小于3的.index文件,这里找到的就是在第一个segment文件。

2、找到的 segment 中的.index文件,用查找的offset 减去.index文件名的offset,也就是00000.index文件,我们要查找的offset为3的message在该.index文件内的索引为3(index采用稀疏存储的方式,它不会为每一条message都建立索引,而是每隔4k左右,建立一条索引,避免索引文件占用过多的空间。缺点是没有建立索引的offset不能一次定位到message的位置,需要做一次顺序扫描,但是扫描的范围很小)。

3、 根据找到的相对offset为3的索引,确定message存储的物理偏移地址为756。

4、 根据物理偏移地址,去.log文件找相应的Message

Kafka 的 Message 存储采用了分区,磁盘顺序读写,分段和稀疏索引等一些手段来达到高效性,在0.9版本之后,offset 已经直接维护在kafka集群的__consumer_offsets这个topic中。