kafka中的offset

本文介绍了Kafka消息系统的架构和核心概念,包括消息传递保障机制、broker的角色、topic的物理分组方式及其与partition、segment、offset之间的关系,并详细解释了如何通过offset定位消息。

官方文档定义:kafka是一个分布式、可分区、多副本的日志系统。

kafka术语:

  • massage: kafka中最基本的传递对象,有固定格式。
  • topic: 一类消息,如page view,click行为等。
  • producer: 产生信息的主体,可以是服务器日志信息等。
  • consumer: 消费producer产生话题消息的主体。
  • broker: 消息处理结点,多个broker组成kafka集群。
  • partition: topic的物理分组,每个partition都是一个有序队列。
  • segment: 多个大小相等的段组成了一个partition。
  • offset: 一个连续的用于定位被追加到分区的每一个消息的序列号,最大值为64位的long大小,19位数字字符长度。

对于一个消息系统而言,追踪客户消费了什么,也即消息消费状态是每个消息系统必须提供的关键功能之一。
系统可以提供的几种可能消息传递保障有3种:

  • At most once 消息至多会被发送一次,但如果产生网络延迟等原因消息就会有丢失。
  • At least once 消息至少会被发送一次,上面既然有消息会丢失,那么给它加一个消息确认机制即可解决,但是消息确认阶段也还会出现同样问题,这样消息就有可能被发送两次。
  • Exactly once 消息只会被发送一次,这是我们想要的效果。

那么kafka是怎么解决的呢?
kafka的解决方案:

  • broker将数据流划分为一组互相独立的分区。这些分区的语义由producer定义,由producer指定每条消息属于哪个分区。一个分区内的消息以到达broker的时间为准排序,将来按此顺序将消息发送给consumer。这样一来,就用不着为每一条消息保存一条元数据(比如标记该消息已使用)了,我们只需要为producer、topic、partition的每种组合记录一个“最高水位标记”(high water mark)即可。我们把这个最高水位标记称作偏移量offset。

topic、partition、segment、offset的关系:

  • partition、segment、offset都是为topic服务的,每个topic可以分为多个partition,一个partition相当于一个大目录,每个partition下面有多个大小相等的segment文件,这个segment是由message组成的,而每一个的segment不一定由大小相等的message组成。segment大小及生命周期在server.properties文件中配置。offset用于定位位于段里的唯一消息。

topic、partition、segment、offset的关系

接下来弄清楚segment具体细节之后再说offset:

  • segment由index和data文件组成,两个文件成对出现,分别存储索引和数据。
  • segment文件命名规则:对于所有的partition来说,segment名称从0开始,之后的每一个segment名称为上一个segment文件最后一条消息的offset值。

那么对于分区中的一个offset例如等于345552怎么去查找相应的message呢?

  • 先找到该message所在的segment文件,通过二分查找的方式寻找小于等于345552的offset,假如叫S的segment符合要求,如果S等于345552则S上一个segment的最后一个message即为所求;如果S小于345552则依次遍历当前segment即可找到。

实际上offset的存储采用了稀疏索引,这样对于稠密索引来说节省了存储空间,但代价是查找费点时间。
稀疏索引与稠密索引

### Kafka 手动提交 offset 的实际提交间隔 在 Kafka 中,当消费者将 offset 提交方式设置为手动提交时,offset 的提交行为不再由 Kafka 自动触发,而是完全由开发者控制。这意味着 offset 的提交时机和频率取决于应用程序的逻辑实现。 最常见的做法是通过调用 `commitSync()` 或 `commitAsync()` 方法来手动提交 offset。其中: - **`commitSync()`** 是同步提交方法,会阻塞当前线程直到提交完成或发生异常。这种方式更可靠,但会影响消费者的吞吐量[^3]。 - **`commitAsync()`** 是异步提交方法,不会阻塞主线程,适合对性能要求较高的场景。然而,它不保证提交一定成功,因此需要配合回调函数进行错误处理。 由于手动提交的频率完全由代码逻辑决定,因此没有固定的“默认间隔”可以参考。常见的实现方式包括: - 每消费一批消息后提交一次 offset; - 在每次处理完特定数量的消息后提交; - 根据时间周期主动触发提交(例如每 10 秒提交一次); - 在消费者关闭前提交一次最终的 offset。 以下是一个典型的 Java 示例,展示如何在拉取消息后手动提交 offset: ```java while (true) { ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofMillis(100)); for (ConsumerRecord<String, String> record : records) { // 处理消息逻辑 } // 手动提交 offset kafkaConsumer.commitAsync(); // 异步提交 // 或者使用同步提交 // kafkaConsumer.commitSync(); } ``` 这种机制允许开发者根据业务需求灵活地控制 offset 提交行为,从而避免自动提交可能引发的数据重复或漏消费问题[^2]。 ### 手动提交的优势与风险 - **优势**: - 更精确地控制 offset 提交时机,确保“恰好一次”语义成为可能; - 避免自动提交中可能出现的“已提交 offset 但未消费消息”的情况,防止数据丢失或重复消费[^2]。 - **风险**: - 如果提交频率过低,消费者崩溃后可能导致大量消息需要重新消费; - 同步提交会显著影响性能,尤其是在高吞吐量场景下; - 异步提交缺乏确认机制,可能无法及时感知提交失败的情况[^3]。 ### 实际应用建议 为了平衡性能与可靠性,通常建议采用如下策略: - 在处理完一批消息后立即调用 `commitAsync()`,并在必要时结合回调处理提交失败的情况; - 对于关键业务逻辑,在确保消息被持久化或处理完成后才提交 offset; - 在消费者启动时读取上次提交的 offset,以支持从上次状态恢复的能力[^4]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值