Kafka 记录

2025年5月14日 | 11 分钟阅读
Kafka Record

Apache Kafka 是一个备受推崇的事件流平台,它构成了许多现代信息处理架构的核心。Kafka 的效率源于其设计原则,它支持实时记录流、容错和可扩展性。Kafka 记录对于这些能力至关重要,因为它是 Kafka 生产者和消费者之间传输的基本数据单元。

在本详尽的指南中,我们将涵盖 Kafka 记录的每一个详细元素,探讨其结构、处理和利用方式,以支持高吞吐量、可扩展且可靠的数据管道。

1. 什么是 Kafka 记录?

从本质上讲,Kafka 记录是在 Kafka 分布式系统中生产者(发送数据的应用程序)和消费者(读取数据的应用程序)之间交换的消息。每条 Kafka 记录包含:

  • Payload(载荷):正在传输的实际数据(消息)。
  • Metadata(元数据):用于在 Kafka 系统中标识、跟踪和管理消息的信息。

Kafka 数据被发布到主题(topic)中,主题是组织消息的逻辑类别。每个主题又被划分为分区(partition),这既保证了可扩展性(通过在多个代理之间分配负载),也保证了容错性(通过复制分区)。

Kafka 记录的特点

  1. 不可变性:一旦写入分区,记录就无法被修改。
  2. 分布式:Kafka 数据分布在多个分区中,以实现水平扩展。
  3. 持久性:Kafka 确保数据被保留可配置的时间,或直到达到存储限制。
  4. 有序性:Kafka 严格维护每个分区内数据的顺序,通过使用偏移量(offset)。

Kafka 记录是构建基于 Kafka 的数据管道的基石,因此它们被优化用于事件驱动架构和实时数据处理。

2. Kafka 记录的结构

Kafka 记录由多个字段组成,每个字段都对数据在 Kafka 中的组织、传输和处理方式做出了贡献。让我们更详细地了解 Kafka 记录的结构。

Kafka 记录结构

元数据载荷
Offset键 (可选)
分区值 (必需)
Timestamp
头 (可选)

元数据包含 Kafka 用于在系统中管理和跟踪数据的字段。

  • 偏移量(Offset):分区内每条记录的唯一标识符。
  • 分区(Partition):记录存储在其中的主题内的逻辑分区。
  • 时间戳(Timestamp):记录生成或摄入的时间。
  • 头(Headers):与记录关联的可选元数据键值对。

载荷(Payload)是消息的实际内容,它包括:

  • 键(Key):可选。决定记录的分区。
  • 值(Value):记录的核心数据或内容。

3. Kafka 记录的组成部分

Kafka 记录包含几个关键组成部分,每个组成部分都起着独特的作用。让我们逐一分解这些组成部分,以便更清晰地理解。

3.1 偏移量 (Offset)

偏移量是一个数字标识符,表示记录在 Kafka 分区中的位置。当新记录被追加到分区时,都会被分配下一个连续的偏移量。偏移量在分区内是唯一的,确保 Kafka 保持严格的记录处理顺序。

  • 偏移量跟踪对消费者至关重要,他们使用偏移量来跟踪已经处理过的数据。Kafka 通过在一个特殊的消费者偏移量主题中存储偏移量来自动管理这一点。

示例

输出

Offset = 20, Key = user001, Value = login_event
Offset = 21, Key = user002, Value = logout_event

在此示例中,消费者读取记录并打印偏移量、键和值。偏移量确保数据以正确的顺序被消费。

3.2 键 (Key)

键是 Kafka 记录的可选部分,通常用于确定记录应写入哪个分区。Kafka 使用基于键的分区来保证具有相同键的每个数据块始终被路由到同一个分区,从而保持其顺序。

  • 当启用日志压缩时,Kafka 会保留每个特定键的最新记录,这使得键成为有状态用例(例如,跟踪用户配置文件或会话数据)的关键组成部分。

键通常被序列化为字符串,但也可以是任何可序列化的对象。

键示例

在此示例中,“user123”是键,它确保与该用户相关的每个事件都将被路由到同一个分区。

3.3 值 (Value)

值是 Kafka 记录的核心内容或数据。它可以包含任何类型的数据,从简单的文本字符串到更复杂的序列化对象,如 JSON、Avro 或 Protocol Buffers。

值是 Kafka 记录中由生产者生成并发送、由消费者接收和处理的部分。应用程序的实际业务逻辑通常与记录值中包含的数据相关联。

3.4 时间戳 (Timestamp)

Kafka 记录包含一个时间戳字段,它记录了事件发生的时间或 Kafka 接收到该记录的时间。Kafka 使用两种类型的时间戳:

  1. 事件时间(Event time):事件发生的实际时间,由生产者提供。
  2. 摄入时间(Ingestion time):Kafka 接收到记录的时间(如果未提供事件时间,则为默认行为)。

在实时系统中,时间戳对于根据事件发生的时间来处理事件非常重要。

示例:访问记录时间戳

3.5 头 (Headers)

头是附加到 Kafka 记录的可选元数据键值对。它们允许将辅助信息(如关联 ID、跟踪信息或编码元数据)与数据关联,而无需修改主载荷。

每个头包含一个键(字符串)和一个值(字节数组)。头在分布式系统中尤其有用,这些系统中需要元数据来进行路由、跟踪或日志记录。

生产者示例:添加头

在此场景中,一个带有关联 ID 的头被添加到 Kafka 记录中。这对于跨微服务或分布式应用程序跟踪消息流非常有用。

4. 序列化和反序列化

序列化和反序列化是 Kafka 中的关键过程,因为它们允许 Kafka 通过网络传输复杂数据(如 JSON、Avro 或 Protocol Buffers)并将数据正确地存储在磁盘上。

4.1 序列化 (Serialization)

序列化是将 Kafka 记录的键和值转换为字节流的过程,Kafka 可以通过网络发送该字节流或将其写入磁盘。Kafka 为常见数据类型(例如字符串、整数和字节数组)提供了内置的序列化器。此外,还可以实现自定义序列化器来处理更复杂的数据格式。

生产者示例:序列化 Kafka 记录

在这里,键(“user123”)和值(“login_event”)都被序列化为字符串,然后才传输到 Kafka。

4.2 反序列化 (Deserialization)

在消费者端,反序列化是相反的过程,即将字节流转换回其原始数据格式(例如,字符串、整数或复杂对象)。Kafka 提供了自动处理此转换的反序列化器。

消费者示例:反序列化 Kafka 记录

在此示例中,反序列化器自动将序列化的字节流转换回键和值字段的字符串。

5. 记录批次和日志段

Kafka 不会将单个记录写入磁盘;相反,它使用记录批次来组织多个记录并将它们一起写入。这种方法通过减少磁盘 I/O 操作和网络开销来提高吞吐量。

5.1 记录批次 (Record Batches)

记录批次是从生产者发送到代理的记录集合。批处理允许 Kafka 分摊网络往返和磁盘写入的成本,从而提高整体系统性能。每个批次的长度可以通过 `batch.Length` 配置进行控制。

通过将数据分组到批次中,Kafka 最小化了单个记录传输的开销。

5.2 日志段 (Log Segments)

Kafka 将记录存储在日志段中,这些日志段是磁盘上的文件。Kafka 中的每个分区由多个日志段组成,这些日志段按顺序存储数据。这种分段机制通过将分区的日志划分为更小、可管理的文件来优化 Kafka 的磁盘使用。

示例:日志段结构

分区 0

日志段 1日志段 2日志段 3

每个日志段存储一系列 Kafka 记录,一旦一个段已满或已超过保留策略,就会创建一个新段。

6. 使用日志压缩处理记录

日志压缩是 Kafka 管理数据和维护高效存储系统的一种机制。虽然日志保留会根据时间和长度限制删除旧数据,但日志压缩会保留每个特定键的最新记录。这使得 Kafka 非常适合跟踪数据最新状态的应用程序,例如用户配置文件或配置设置。

6.1 什么是日志压缩?

日志压缩确保 Kafka 保留每个特定键的至少最新值,即使存在该记录的旧版本。这使得 Kafka 非常适合跟踪数据当前状态的应用程序,例如用户配置文件或配置设置。

例如,考虑一个跟踪用户活动的应用程序。Kafka 可以配置为仅保留每个用户的最新活动记录,同时丢弃具有相同键的旧数据。

6.2 日志压缩的工作原理

  1. 启用日志压缩后,Kafka 会定期扫描日志段以查找具有相同键的数据。
  2. 它会保留每个键的最新记录,丢弃所有具有匹配键的旧数据。
  3. 随着新记录的写入,这个过程会持续进行,确保有效利用存储空间,同时为每个键维护最新状态。

6.3 日志压缩的优势

  • 高效存储:通过删除冗余或过时数据来减少磁盘使用。
  • 有状态用例:非常适合应用程序,其中实体的最新状态(例如,用户配置文件、配置)是最重要的。
  • 容错性:即使在故障或重新处理后,也能保证保留最新状态。

日志压缩示例

让我们考虑一个跟踪用户登录活动的 Kafka 主题。

压缩前

用户 ID活动
user001已登录 (偏移量 1)
user001已退出 (偏移量 2)
user002已登录 (偏移量 3)
user003已登录 (偏移量 4)
user003已退出 (偏移量 5)

压缩后

用户 ID活动
user001已退出 (偏移量 2)
user002已登录 (偏移量 3)
user003已退出 (偏移量 5)

在此示例中,日志压缩后,将保留每个用户的最新活动,从而减小了存储占用空间。

7. 使用 KafkaProducer 生产记录

KafkaProducer API 用于向 Kafka 主题生产和发送数据。生产者负责序列化数据的键和值,并确保它们被路由到主题的正确分区。

7.1 生产者如何工作

  • 序列化:生产者在将每条记录的键和值发送到 Kafka 之前进行序列化。这可以使用内置序列化器或自定义序列化器来完成。
  • 分区:如果为记录指定了键,生产者会使用分区器来确定记录将被发送到哪个分区。如果未提供键,则数据将循环分布在可用分区中。
  • 重试和确认:生产者可以配置为在发送失败时重试发送数据,并等待代理的确认(ack),以确保数据的可靠传递。

7.2 生产者示例:发送 Kafka 记录

以下是使用 KafkaProducer API 发送数据的示例。

在这种情况下

生产者将一条记录发送到“user_activity”主题。

键(“user123”)确保记录被路由到一致的分区。

回调函数处理代理的响应,并在发送成功时打印有关记录的元数据。

7.3 批处理和 linger.ms

Kafka 生产者会批量处理数据,以通过减少网络调用次数来提高性能。批处理允许多个记录在单个请求中发送。

通过设置 `linger.Ms`,您可以配置生产者在发送批次之前等待其他数据一段特定的时间。这会减少请求的数量,但会以轻微的延迟增加为代价。

8. 使用 KafkaConsumer 消费记录

KafkaConsumer API 用于从 Kafka 主题读取(或消费)数据。消费者轮询新数据,并以拉取模式处理它们,这允许它们控制记录的消费速度。

8.1 消费者如何工作

  • 反序列化:消费者使用配置的反序列化器自动反序列化数据的键和值。
  • 分区分配:Kafka 将分区分配给消费者组中的消费者,以确保每个分区一次只被一个消费者消费。
  • 偏移量管理:消费者跟踪他们处理的最后一条记录的偏移量,以确保他们在重新启动时能从正确的位置继续。

8.2 消费者示例:消费 Kafka 记录

在这种情况下,消费者订阅“user_activity”主题,并每 100 毫秒轮询一次新数据。对于每条记录,它会打印出键、值、分区和偏移量。

8.3 再均衡和分区分配

当消费者组的成员发生变化时(例如,新的消费者加入或现有的消费者离开),Kafka 会执行再均衡操作,将分区重新分配给消费者。再均衡确保工作负载在组内的可用消费者之间均匀分布。

9. Kafka 记录元数据

Kafka 记录带有丰富的元数据,生产者和消费者都可以访问。这些元数据包括主题、分区、偏移量和时间戳等信息。

9.1 RecordMetadata 类

当生产者发送一条记录时,它会收到一个 `RecordMetadata` 对象,其中包含有关记录存储位置的详细信息。

  • 主题:记录发送到的主题。
  • 分区:记录存储的分区。
  • 偏移量:记录在分区内的唯一标识符。
  • 时间戳:Kafka 生产或摄入记录的时间。

示例:访问记录元数据

在此示例中,发送记录后,生产者使用元数据打印记录的主题、分区和偏移量。

10. 总结

  • Kafka 记录包含键、值、时间戳以及偏移量和分区等元数据。
  • 记录由 `KafkaProducer` 生产,由 `KafkaConsumer` 消费。
  • 记录被序列化为字节流以进行传输,并在消费时进行反序列化。
  • 记录被分组到批次中以优化性能并减少网络开销。

11. 结论

在 Kafka 中,记录是基本的数据单元,它承载着表示在 Kafka 事件流平台中流动的数据的键值对。从通过 `KafkaProducer` 生产到通过 `KafkaConsumer` 消费,数据都遵循一个包含序列化、分区、批处理和存储在日志段中的路径。Kafka 的架构针对高吞吐量、低延迟的数据处理进行了优化,通过支持各种序列化格式、基于键的精确分区以及日志压缩等数据保留策略,提供了灵活性。理解 Kafka 如何处理数据对于构建可扩展、容错且高效的事件驱动应用程序至关重要。