kafka消息存储

broker、topic、partition

kafka 的数据分布是一个 3 级结构,依次为 broker、topic、partition。

也可以理解为数据库的分库分表,然后还有记录这么一个结构。

broker 可以看作是 kafka 集群中的一个节点,可以理解为一台服务器,是对 kafka 集群中节点的抽象。多个 broker 就组成了 kafka 分布式集群。

topic 主题,可以理解为分类、队列等等,是对 broker 的进一步细分,便于对数据的管理。topic 可以分布到不同的 broker 上。

一个 topic 主题可以分为多个 partition 分区。

partition分区配置

在创建 topic 时,可以在配置文件中($KAFKA_HOME/config/server.properties)设置 partition 的数量。也可以在 topic 创建后修改 partition 的数量。

# The default number of log partitions per topic. More partitions allow greater
# parallelism for consumption, but this will also result in more files across
# the brokers.
num.partitions=3

kafka 中的消息数据最终都是存储在 partition 里。

partition分区

partition 是以文件的形式存储数据的。

文件存放位置,可以通过配置文件 server.properties 中的 log.dirs 指定。

文件命名,假如你新建了一个名为 student-log 的主题,partition 指定为 3 个,那么目录结构为:

student-log-0
student-log-1
student-log-2

在 kafka 中,同一个 topic 下有多个不同的 partition,每个 partition 就是一个目录。可以看到 partition 的目录命名规则:

topic名称+有序序号

第一个序号从 0 开始,最大的序号为 partition 数量减 1

partition 是实际物理上的概念,而 topic 是逻辑上的概念。可以理解为实际存储数据里并没有 topic 的文件夹,而只有 partition 的目录文件。

那 partition 目录里又有什么东西?

真正存储的数据。

而且它对 partition 又进一步的细分为 segment。segment 又是什么东西?

可以这样理解,一般存储为文件系统,如果只有一个文件来存储数据,那么随着时间越长数据越来越多,文件大小就会变得越来越大,必然会导致写入、查询文件都变得越来越慢。

就像写一个日志系统,文件大了,就会按照一定规则将大文件分割成小文件来进行读写。

实际就是一种细分的思想。

每个 partition(目录)相当于一个大型文件被细分到多个大小相等的 segment 文件中。

每个 segment 中的消息数量不一定相等。

segment存储

segment 的文件配置项:

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

segment 文件是由 2 部分组成,分别为 .index 和 .log 结尾的文件:

  • index 结尾文件表示索引文件。
  • log 结尾文件表示数据文件。

这个 2 个文件的命名规则:

partition 全局的第一个 segment 从 0 开始,后续每个 segment 文件名为上一个segment 文件最后一条消息的 offset 值。

数值大小为 64 位,20 位数字字符长度,没有数字用 0 填充

比如:

00000000000000000000.index
00000000000000000000.log
00000000000000160420.index
00000000000000160420.log
00000000000000207460.index
00000000000000207460.log

00000000000000160420.index

00000000000000160420.log

上面 2 个文件中的 160420 表示该文件中 offset 从 160421 开始。那么00000000000000000000.index (.log) 的 offset 结束位置就是 160420。也就是命名规则,后面每个 segment 文件名为上一个 segment 文件最后一条消息的 offset 值。

如图所说示,.index 索引文件存储了索引数据,.log 文件存储了大量的消息数据。

索引文件中 [3, 348] , 3 在 .log 文件中表示第 3 个消息,那么在全局 partition 表中表示为 160420 + 3 = 160423。

348 表示该消息的物理偏移地址。

怎么在 .log 文件中查找数据呢?

比如查找 offset = 160428 的消息数据。

先查找 segment 文件:

00000000000000000000.index 为最开始的文件,

00000000000000160420.index 为第二个文件,它的起始位置偏移量 offset: 160420+1

00000000000000207460.index 为第三个文件,它的起始位置偏移量 offset: 207460+1

所以 offset = 160428 就落在了第二个文件中。

从图中可以看出,索引并不是为每一条 message 都建立了索引,而是每隔一定字节数建立索引,这样避免索引占用太多空间。缺点是需要一次扫描文件并查找。不过可以根据二分查找快速定位文件位置。

在根据索引文件中的 [8, 1325] 定位到 00000000000000160420.log 文件中 1325 的位置进行读取。

开始读取后,那何时读取消息结束?

一条数据读到哪里结束才能完整的读取一条数据,而不读取多余的数据。这就涉及到一条消息的消息体结构了。

每一条消息都有固定的消息结构,就好像 http 消息体有一个结构一样,kafka 中消息体结构为:

offset(8 Bytes)- 消息偏移量

message size(4 Bytes)- 消息体的大小

crc32(4 Bytes)- crc32 编码校验和

magic(1 Byte)- 本次发布Kafka服务程序协议版本号

attributes(1 Byte)- 表示为独立版本、或标识压缩类型、或编码类型

key length(4 Bytes)- 消息Key的长度

key(K Bytes)- 消息Key的实际数据

payload(N Bytes)- 消息的实际数据

补充说明:

上面数据格式是 kafka 0.11 前的格式,具体说明看这里:Old Message Format

根据消息体结构就可以确定一条消息的大小,就可以读取到哪里截止。

kafka 0.11 以后的格式是啥,可以看下面的介绍。

消息格式

Record Batch

kafka 0.11 后的消息格式,消息集合对应的类,clients/src/main 目录下:

kafka v3.0

org.apache.kafka.common.record.DefaultRecordBatch

org.apache.kafka.common.record.RecordBatch

// common.record.DefaultRecordBatch.java

/**
* RecordBatch implementation for magic 2 and above. The schema is given below:
*
* RecordBatch =>
* BaseOffset => Int64
* Length => Int32
* PartitionLeaderEpoch => Int32
* Magic => Int8
* CRC => Uint32
* Attributes => Int16
* LastOffsetDelta => Int32 // also serves as LastSequenceDelta
* FirstTimestamp => Int64
* MaxTimestamp => Int64
* ProducerId => Int64
* ProducerEpoch => Int16
* BaseSequence => Int32
* Records => [Record]

RecordBath 里包含很多 records 。

结构图示:

字段 类型 说明
baseOffset int64 当前 RecordBatch起始位置。Record 中的 offset delta与该baseOffset相加才得到真正的offset值
batchLength int32 RecordBatch 总长
partitionLeaderEpoch int32 标记 partition 中 leader replica 的元信息
magic int8 版本的魔术值,V2版本魔术值为2
crc int32 校验码, 效验部分从开始到结束全部数据,但除了partitionLeaderEpoch值
attributes int16 消息属性,0~2:表示压缩类型 第3位:时间戳类型 第4位:是否是事务型记录 5表示ControlRecord,这类记录总是单条出现,它只在broker内处理
lastOffsetDelta int32 RecordBatch 最后一个 Record 的相对位移,用于 broker 确认 RecordBatch 中 Records 的组装正确性
firstTimestamp int64 RecordBatch 第一条 Record 的时间戳
maxTimestamp int64 RecordBatch 中最大的时间戳,一般情况下是最后一条Record的时间戳,用于 broker 判断 RecordBatch 中 Records 的组装是否正确
producerId int64 生产者编号,用于支持幂等性(Exactly Once 语义)
producerEpoch int16 同producerEpoch,支持幂等性
baseSequence int32 同上,支持幂等性,也用于效验是否重复Record
records [Record]

幂等性的详情可以参考这里:Exactly+Once+Delivery+and+Transactional+Messaging

record

kafka 0.11.0 版本开始使用的消息格式。

kafka v3.0。

Record消息,clients/src/main 目录下 :

org.apache.kafka.common.record.DefaultRecord

org.apache.kafka.common.record.Record

// common.record.DefaultRecord.java

/**
* This class implements the inner record format for magic 2 and above. The schema is as follows:
*
*
* Record =>
* Length => Varint
* Attributes => Int8
* TimestampDelta => Varlong
* OffsetDelta => Varint
* Key => Bytes
* Value => Bytes
* Headers => [HeaderKey HeaderValue]
* HeaderKey => String
* HeaderValue => Bytes

recocd 消息结构图:

可以参考这里,kafka doc record:https://kafka.apache.org/documentation/#record

Record 消息中的关键字,字段类型好多都是采用了 varints,动态类型,这样有利于kafka根据具体的值来确定需要几个字节保存。

record 消息字段说明:

字段 类型 说明
length varints 消息中长度
attributes int8 unused,没有使用了,但仍占据了1B大小
timestamp delta varlong 时间戳增量。一般占据8个字节
offset delta varint 位移增量
key length varint key的长度
key byte[] key的值
valuelen varint value值的长度
value byte[] value的实际值
headers [header] 头部结构,支持应用级别的扩展

header 头部结构组成

字段 类型 说明
headerKeyLength varints 头部key的长度
headerKey string 头部 key值
headerValueLength varint 头部值的长度
value byte[] header的值

header 更多信息可以参考这里:

https://cwiki.apache.org/confluence/display/KAFKA/KIP-82+-+Add+Record+Headers

根据这篇文章总结:

v2 版本的消息结构设计,不仅增加了一些功能如事务、幂等等功能,也用变长类型优化了整体空间存储,当然还有压缩算法。

其中字段类型设计参考了Protocol Buffer,引入变长类型(Varints)和 ZigZag 编码。详情查看

https://developers.google.com/protocol-buffers/docs/encoding

这些优化都是为了kafka更好用而做的设计上的努力。

kafka 的一些消息协议、消息格式、api,protocol 可以参考这里,内容比较详尽:

A Guide To The Kafka Protocol

参考

kafka protocol

kafka messages

kafka old message format

kafka github

kafka 消息格式演变

kafka-producer-consumer-overview

kafka学习笔记02-kafka消息存储的更多相关文章

  1. Kafka学习笔记之Kafka三款监控工具

    0x00 概述 在之前的博客中,介绍了Kafka Web Console这 个监控工具,在生产环境中使用,运行一段时间后,发现该工具会和Kafka生产者.消费者.ZooKeeper建立大量连接,从而导 ...

  2. Kafka学习笔记之Kafka性能测试方法及Benchmark报告

    0x00 概述 本文主要介绍了如何利用Kafka自带的性能测试脚本及Kafka Manager测试Kafka的性能,以及如何使用Kafka Manager监控Kafka的工作状态,最后给出了Kafka ...

  3. Kafka学习笔记之Kafka Consumer设计解析

    0x00 摘要 本文主要介绍了Kafka High Level Consumer,Consumer Group,Consumer Rebalance,Low Level Consumer实现的语义,以 ...

  4. Kafka学习笔记之Kafka背景及架构介绍

    0x00 概述 本文介绍了Kafka的创建背景,设计目标,使用消息系统的优势以及目前流行的消息系统对比.并介绍了Kafka的架构,Producer消息路由,Consumer Group以及由其实现的不 ...

  5. Kafka学习笔记1——Kafka的安装和启动

    一.准备工作 1. 安装JDK 可以用命令 java -version 查看版本

  6. Kafka学习笔记之Kafka High Availability(下)

    0x00 摘要 本文在上篇文章基础上,更加深入讲解了Kafka的HA机制,主要阐述了HA相关各种场景,如Broker failover,Controller failover,Topic创建/删除,B ...

  7. Kafka学习笔记之Kafka High Availability(上)

    0x00 摘要 Kafka在0.8以前的版本中,并不提供High Availablity机制,一旦一个或多个Broker宕机,则宕机期间其上所有Partition都无法继续提供服务.若该Broker永 ...

  8. Kafka学习笔记之Kafka自身操作日志的清理方法(非Topic数据)

    0x00 概述 本文主要讲Kafka自身操作日志的清理方法(非Topic数据),Topic数据自己有对应的删除策略,请看这里. Kafka长时间运行过程中,在kafka/logs目录下产生了大量的ka ...

  9. Kafka学习笔记之Kafka日志删出策略

    0x00 概述 kafka将topic分成不同的partitions,每个partition的日志分成不同的segments,最后以segment为单位将陈旧的日志从文件系统删除. 假设kafka的在 ...

  10. 【kafka学习笔记】kafka的基本概念

    在了解了背景知识后,我们来整体看一下kafka的基本概念,这里不做深入讲解,只是初步了解一下. kafka的消息架构 注意这里不是设计的架构,只是为了方便理解,脑补的三层架构.从代码的实现来看,kaf ...

随机推荐

  1. [转帖]K8S 挂载 minio csi 的方式.

    对象存储   前置条件 安装Minio(在102主机上操作) 安装csi-s3插件(在103主机上操作) 使用 参考 本文介绍kubernetes如何基于对象存储(minio)创建PV与PVC 前置条 ...

  2. [转帖]Optimizing Block Device Parameter Settings of Linux

    https://support.huawei.com/enterprise/en/doc/EDOC1000181485/ddbc0e8b/optimizing-block-device-paramet ...

  3. [转帖]Linux命令拾遗-动态追踪工具

      原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介# 这是Linux命令拾遗系列的第六篇,本篇主要介绍工作中常用的动态追踪工具strace.arthas.bpft ...

  4. MySQL数据库存储varchar时多大长度会出现行迁移?

    最近客户现场有人问过mysql数据库的一些参数配置的问题, 这边数据库需要将strict 严格模式关掉, 目的是为了保证数据库在插入字段时不会出现8126的长度限制错误问题. 但是一直很困惑, mys ...

  5. rabbitmq rpm包安装以及简单设置用户的方法

    公司有一台性能比较好的power机器. 同事要求安装rabbitmq 今天尝试进行了一下处理 公司里面有网络有相应的源 性能还不错 第一步安装一下依赖的erlang yum install erlan ...

  6. Rendezvous hashing算法介绍

    Rendezvous hashing Rendezvous hashing用于解决分布式系统中的分布式哈希问题,该问题包括三部分: Keys:数据或负载的唯一标识 Values:消耗资源的数据或负载 ...

  7. 【JS 逆向百例】某公共资源交易网,公告 URL 参数逆向分析

    声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:某地公共资 ...

  8. wap2app下拉刷新

    支持全局刷新,支持vue项目 目前支持wap2app,uin-app全局下拉刷新 戳我阅读原文 --转载自微信公众号:酿俗

  9. 数据挖掘机器学习[二]---汽车交易价格预测详细版本{EDA-数据探索性分析}

    题目出自阿里天池赛题链接:零基础入门数据挖掘 - 二手车交易价格预测-天池大赛-阿里云天池 相关文章: 特征工程详解及实战项目[参考] 数据挖掘---汽车车交易价格预测[一](测评指标:EDA) 数据 ...

  10. 从嘉手札<2023-12-09>

    大雪时节 有种风雪欲来的静谧 如同飘摇的浮舟 人们常说上岸 可对于常年生活在水里的鱼儿来说 哪里是岸边呢 我不知道未来 但唯一可以确定的是 无论你过的怎么样 你都需要给自己一个交待 哪怕风雪兼程 哪怕 ...