深入浅出理解kafka存储机制

前言


Kafka 是为了解决大数据的实时日志流而生的, 每天要处理的日志量级在千亿规模。对于日志流的特点主要包括 :

数据实时产生。
海量数据存储与处理。
所以它必然要面临分布式系统遇到的高并发、高可用、高性能等三高问题。

对于 Kafka 的存储需要保证以下几点:

存储的主要是消息流(可以是简单的文本格式也可以是其他格式)。
要支持海量数据的高效存储、高持久化(保证重启后数据不丢失)。
要支持海量数据的高效检索(消费的时候可以通过offset或者时间戳高效查询并处理)。
要保证数据的安全性和稳定性、故障转移容错性。

 

一、Kafka 存储选择


磁盘的顺序I/O性能要强于内存的随机I/O性能。如果需要较高的存储性能,必然是提高读速度和写速度:

提高读速度:利用索引,来提高查询速度,但是有了索引,大量写操作都会维护索引,那么会降低写入效率。常见的如关系型数据库:mysql等。
提高写速度:这种一般是采用日志存储, 通过顺序追加(批量)写的方式来提高写入速度,因为没有索引,无法快速查询,最严重的只能一行行遍历读取。常见的如大数据相关领域的基本都基于此方式来实现。

这里有个疑惑点就是什么是日志存储,日志存储可不是日志,只是打日志的形式,每次打日志

就会在后面追加一条记录,只是个存储形式

二、Kafka 存储方案剖析


对于 Kafka 来说, 它主要用来处理海量数据流,这个场景的特点主要包括:

写操作:写并发要求非常高,基本得达到百万级 TPS,顺序追加写日志即可,无需考虑更新操作。
读操作:相对写操作来说,比较简单,只要能按照一定规则高效查询即可(offset或者时间戳)。
对于写操作来说,直接采用顺序追加写日志的方式就可以满足 Kafka 对于百万TPS写入效率要求。重点在如何解决高效查询这些日志。Kafka采用了稀疏哈希索引(底层基于Hash Table 实现)的方式。

 

把消息的 Offset 设计成一个有序的字段,这样消息在日志文件中也就有序存放了,也不需要额外引入哈希表结构, 可以直接将消息划分成若干个块,对于每个块,我们只需要索引当前块的第一条消息的Offset (类似二分查找算法的原理),即先根据 Offset 大小找到对应的块, 然后再从块中顺序查找,这样就可以快速定位到要查找的消息。
 

一个Topic对应多个partition,一个partition对应多个segment,一个segment有.log/.index/.timeindex等文件。

由于生产者生产的消息会不断追加到 log 文件末尾,为防止 log 文件过大导致数据定位效率低下,Kafka采取了分片和索引机制。
它将每个 Partition 分为多个 Segment,每个 Segment 对应两个文件:“.index” 索引文件和 “.log” 数据文件。

这些文件位于同一文件下,该文件夹的命名规则为:topic 名-分区号。例如,test这个 topic 有三个分区,则其对应的文件夹为 test-0,test-1,test-2。

$ ls /tmp/kafka-logs/test-0
00000000000000000000.index  00000000000000000000.log  00000000000000000000.timeindex  leader-epoch-checkpoint

index 和 log 文件以当前 Segment 的第一条消息的 Offset 命名。下图为 index 文件和 log 文件的结构示意图:

“.index” 文件存储大量的索引信息;“.log” 文件存储大量的数据。索引文件中的元数据指向对应数据文件中 Message 的物理偏移量。
查看索引:

sh ./kafka-dump-log.sh --files /tmp/kafka-logs/test-0/00000000000000000000.index
Dumping /tmp/kafka-logs/test-0/00000000000000000000.index
offset: 19999 position: 300695
Mismatches in :/tmp/kafka-logs/test-0/00000000000000000000.index
  Index offset: 19999, log offset: 10000
  1. 创建索引文件:在需要建立索引时,创建一个空的索引文件和时间索引文件。

    • 索引文件(.index)存储消息的物理偏移量和其他相关元数据。
    • 时间索引文件(.timeindex)存储消息的时间信息和对应的物理偏移量。
  2. 数据生成:新的消息被写入数据文件(.log 文件),并在写入数据文件之前,将消息的时间信息和对应的物理偏移量记录在时间索引文件中。

  3. 时间索引的二分查找:当需要根据时间查找特定消息时,首先在时间索引文件中进行二分查找,以找到大致的时间范围。

    • 通过比较目标时间与时间索引文件中的中间时间,可以确定目标时间所在的区域。
    • 接着,在该区域进行进一步的二分查找,直到找到目标时间所对应的物理偏移量。
  4. 索引文件的二分查找:使用时间索引文件找到目标消息的物理偏移量后,在索引文件中进行二分查找,以获取消息的更详细信息,如消息的唯一标识符和其他元数据。

  5. 物理偏移量定位:使用查询得到的物理偏移量,将其作为索引,可以直接跳转到数据文件中对应消息的位置。

  6. 数据文件的读取:通过通过物理偏移量,定位到数据文件中对应消息的位置,并读取相应的消息内容。

三、Kafka 存储架构设计


Kafka 最终的存储实现方案:基于顺序追加写日志 + 稀疏哈希索引。
Kafka 是基于「主题 + 分区 + 副本 + 分段 + 索引」的结构:

1.  kafka 中消息是以主题 Topic 为基本单位进行归类的,这里的 Topic 是逻辑上的概念,实际上在磁盘存储是根据分区 Partition 存储的, 即每个 Topic 被分成多个 Partition,分区 Partition 的数量可以在主题 Topic 创建的时候进行指定。
2.  Partition 分区主要是为了解决 Kafka 存储的水平扩展问题而设计的, 如果一个 Topic 的所有消息都只存储到一个 Kafka Broker上的话, 对于 Kafka 每秒写入几百万消息的高并发系统来说,这个Broker 肯定会出现瓶颈, 故障时候不好进行恢复,所以 Kafka 将 Topic 的消息划分成多个Partition, 然后均衡的分布到整个 Kafka Broker 集群中。
3.  Partition 分区内每条消息都会被分配一个唯一的消息 id,即偏移量 Offset,因此kafka 只能保证每个分区内部有序性,并不能保证全局有序性。
为了防止 Log 日志过大,Kafka 又引入了日志分段(LogSegment)的概念,将 Log 切分为多个 4.  4.  LogSegement,相当于一个巨型文件被平均分割为一些相对较小的文件,这样也便于消息的查找、维护和清理。这样在做历史数据清理的时候,直接删除旧的 LogSegement 文件就可以了。
5.  Log 日志在物理上只是以文件夹的形式存储,而每个 LogSegement 对应磁盘上的一个日志文件和两个索引文件,以及可能的其他文件(比如以".snapshot"为后缀的快照索引文件等)。
 

四、Kafka 日志系统架构设计


kafka 消息是按主题 Topic 为基础单位归类的,各个 Topic 在逻辑上是独立的,每个 Topic 又可以分为一个或者多个 Partition,每条消息在发送的时候会根据分区规则被追加到指定的分区中

4.1、Kafka日志目录布局


Log 对应了一个命名为-的文件夹。举个例子,假设现在有一个名为“topic-order”的 Topic,该 Topic 中 有4个 Partition,那么在实际物理存储上表现为“topic-order-0”、“topic-order-1”、“topic-order-2”、“topic-order-3” 这4个文件夹。

Log 中写入消息是顺序写入的。但是只有最后一个 LogSegement 才能执行写入操作,之前的所有LogSegement 都不能执行写入操作。为了更好理解这个概念,我们将最后一个 LogSegement 称 为"activeSegement",即表示当前活跃的日志分段。随着消息的不断写入,当 activeSegement 满足一定的条件时,就需要创建新的 activeSegement,之后再追加的消息会写入新的 activeSegement。
 

为了更高效的进行消息检索,每个 LogSegment 中的日志文件(以“.log”为文件后缀)都有对应的几个索引文件:偏移量索引文件(以“.index”为文件后缀)、时间戳索引文件(以“.timeindex”为文件后缀)、快照索引文件 (以“.snapshot”为文件后缀)。其中每个 LogSegment 都有一个 Offset 来作为基准偏移量(baseOffset),用来表示当前 LogSegment 中第一条消息的 Offset。偏移量是一个64位的Long 长整型数,日志文件和这几个索引文件都是根据基准偏移量(baseOffset)命名的,名称固定为20位数字,没有达到的位数前面用0填充。比如第一个 LogSegment 的基准偏移量为0,对应的日志文件为00000000000000000000.log。

注意每个 LogSegment 中不只包含“.log”、“.index”、“.timeindex”这几种文件,还可能包含“.snapshot”、“.txnindex”、“leader-epoch-checkpoint”等文件, 以及 “.deleted”、“.cleaned”、“.swap”等临时文件。

消费者消费的时候,会将提交的位移保存在 Kafka 内部的主题__consumer_offsets中。

4.2、Kafka磁盘数据存储

Kafka 是依赖文件系统来存储和缓存消息,以及典型的顺序追加写日志操作,另外它使用操作系统的 PageCache 来减少对磁盘 I/O 操作,即将磁盘的数据缓存到内存中,把对磁盘的访问转变为对内存的访问。

在 Kafka 中,大量使用了 PageCache, 这也是 Kafka 能实现高吞吐的重要因素之一, 当一个进程准备读取磁盘上的文件内容时,操作系统会先查看待读取的数据页是否在 PageCache 中,如果命中则直接返回数据,从而避免了对磁盘的 I/O 操作;如果没有命中,操作系统则会向磁盘发起读取请求并将读取的数据页存入 PageCache 中,之后再将数据返回给进程。同样,如果一个进程需要将数据写入磁盘,那么操作系统也会检查数据页是否在页缓存中,如果不存在,则 PageCache 中添加相应的数据页,最后将数据写入对应的数据页。被修改过后的数据页也就变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的一致性。
 

除了消息顺序追加写日志、PageCache以外, kafka 还使用了零拷贝(Zero-Copy)技术来进一步提升系统性能。

五、Kafka 可靠性

Kafka 从拓扑上分有如下角色:

  1. Consumer: 消费者。
  2. Producer: 生产者。
  3. Kafka broker: kafka 集群中的服务器,topic 里的消息数据存在上面。

 

Producer 采用发送 push 的方式将消息发到 broker 上,broker 存储后。由 consumer 采用 pull 模式订阅并消费消息。


5.1、Producer的可靠性保证


生产者的可靠性保证依靠回答: 发消息之后有没有 ack,发消息收到 ack 后,是不是消息就不会丢失了?而 Kafka 通过配置来指定 producer 生产者在发送消息时的 ack 策略:
 

# -1(全量同步确认,强可靠性保证) 
Request.required.acks= -1
# 1(leader 确认收到, 默认) 
Request.required.acks = 1
# 0(不确认,但是吞吐量大) 
Request.required.acks = 0

其中一点就是ack确认机制来保证可靠性

以下是对不同 "Request.required.acks" 参数值的解释:

  1. Request.required.acks = -1(全量同步确认,强可靠性保证):

    • 当生产者发送消息后,需要等待 ISR(In-Sync Replicas,即同步副本) 中所有的副本都确认(包括 leader 和所有的 follower),才会收到 ACK 确认。
    • 这种模式下,消息的可靠性最高,但是会有一定的延迟,因为需要等待所有副本确认完成。
  2. Request.required.acks = 1(Leader 确认收到,默认):

    • 当生产者发送消息后,只需要等待 Leader 副本确认收到消息,生产者就会收到 ACK 确认。
    • 这种模式下,消息的可靠性较高,大部分情况下可以满足需求。
  3. Request.required.acks = 0(不确认,但是吞吐量大):

    • 当生产者发送消息后,生产者将不会等待任何 ACK 确认,直接继续发送下一批消息。
    • 这种模式下,消息的可靠性较低,因为没有确认机制,可能会导致消息丢失。但是由于不需要等待确认,吞吐量更高。

N是follower的数量。

正常情况下,所有 follower 复制完成后,leader 回 producer ack。
异常情况下,如果当数据发送到 leader 后部分副本(f1 和 f2 同步), leader 挂了?此时任何 follower 都有可能变成新的 leader, producer 端会得到返回异常,producer 端会重新发送数据,但这样数据可能会重复(但不会丢失)。

min.insync.replicas 参数用于保证当前集群中处于正常同步状态的副本 follower 数量,当实际值小于配置值时,集群停止服务。如果配置为 N/2+1, 即多一半的数量,则在满足此条件下,通过算法保证强一致性。当不满足配置数时,牺牲可用性即停服。

unclean.leader.election.enable 来控制在有些follower未同步的情况下,是否可以选举未同步的follower为 leader。旧版本中默认为true,在某个版本下已默认为 false,避免这种情况下消息截断的出现。

通过 ack 和 min.insync.replicas 和 unclean.leader.election.enable 的配合,保证在 kafka 配置为 CP系统时,要么不工作,要么得到 ack 后,消息不会丢失且消息状态一致。

5.1.2、kafka 配置为 AP(Availability & Partition tolerance)系统

request.required.acks=1 
min.insync.replicas = 1 
unclean.leader.election.enable = false

通过 producer 策略的配置和 kafka 集群通用参数的配置,可以针对自己的业务系统特点来进行合理的参数配置,在通讯性能和消息可靠性下寻得某种平衡。

5.2、Broker 的可靠性保证


消息落到 broker 后,集群通过何种机制来保证不同副本建的消息状态一致性。
LEO和HW简单介绍:
LEO:LogEndOffset的缩写,表示每个partition的log最后一条Message的位置。
HW: HighWaterMark的缩写,是指consumer能够看到的此partition的位置。 取一个partition对应的ISR中最小的LEO作为HW,consumer最多只能消费到HW所在的位置。

HW用于标识消费者可以读取的最大消息位置,LEO用于标识消息追加到文件的最后位置。
如果消息发送成功,不代表消费者可以消费这条消息。

5.3、Consumer 的可靠性策略

(1) AutoCommit。

enable.auto.commit = true
auto.commit.interval.ms 默认5000 (5 seconds)

 

配置如上的 consumer 收到消息就返回正确给 brocker, 但是如果业务逻辑没有走完中断了,实际上这个消息没有消费成功。这种场景适用于可靠性要求不高的业务。其中 auto.commit.interval.ms 代表了自动提交的间隔。

(2) 手动 Commit。

enable.auto.commit = false

配置为手动提交的场景下,业务开发者需要在消费消息到消息业务逻辑处理整个流程完成后进行手动提交。如果在流程未处理结束时发生重启,则之前消费到未提交的消息会重新消费到,即消息显然会投递多次。此处应用与业务逻辑明显实现了幂等的场景下使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/232506.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《PySpark大数据分析实战》-01.关于数据

📋 博主简介 💖 作者简介:大家好,我是wux_labs。😜 热衷于各种主流技术,热爱数据科学、机器学习、云计算、人工智能。 通过了TiDB数据库专员(PCTA)、TiDB数据库专家(PCTP…

uni-app应用设置 可以根据手机屏幕旋转进行 (横/竖) 屏切换

首先 我们打开项目的 manifest.json 在左侧导航栏中找到 源码视图 然后找到 app-plus 配置 在下面加上 "orientation": [//竖屏正方向"portrait-primary",//竖屏反方向"portrait-secondary",//横屏正方向"landscape-primary",//横屏…

计算目标检测和语义分割的PR

需求描述 实际工作中,相比于mAP项目更加关心的是特定阈值下的precision和recall结果;由于本次的GT中除了目标框之外还存在多边形标注,为此,计算IoU的方式从框与框之间变成了mask之间; 本文的代码适用于MMDetection下的…

kafka常见问题处理

1. 如何防⽌消息丢失 在生产者层面,我们有个ack参数确认机制 设置成-1,也就是副本全部同步了leader才发送ack,这样确保leader和副本挂掉只剩一个还能 保证消息不丢失 消费者: 把⾃动提交改成⼿动提交 2. 如何防⽌重复消费 在…

【数据结构】平衡树引入

数据结构-平衡树 前置知识 二叉树二叉树的中序遍历 问题 维护一个数据结构,支持插入元素、删除元素、查询元素的排名、查询排名对应的元素、查询元素的前驱、查询元素的后继等。 BST(二叉搜索树) 作为一个基本无效(很容易卡掉…

【IC验证】perl脚本——分析前/后仿用例回归情况

目录 1 脚本名称 2 脚本使用说明 3 nocare_list文件示例 4 脚本执行方法 5 postsim_result.log文件示例 6 脚本代码 1 脚本名称 post_analysis 2 脚本使用说明 help:打印脚本说明信息 命令:post_analysis help 前/后仿结束后,首先填…

VoxPoser:使用语言模型进行机器人操作的可组合 3D 值图

语言是一种压缩媒介,人们通过它来提炼和传达他们对世界的知识和经验。大型语言模型(LLMs)已成为一种有前景的方法,通过将世界投影到语言空间中来捕捉这种抽象。虽然这些模型被认为在文本形式中内化了可概括的知识,但如…

C++STL详解+代码分析+典例讲解

vector 的介绍: 1、vector是表示可变大小数组的序列容器。 2、vector就像数组一样,也采用的连续空间来存储元素,这也意味着可以采用下标对vector的元素进行访问。 3、vector与普通数组不同的是,vector的大小是可以动态改变的。 4、…

基于K-means与CNN的遥感影像分类方法

基于K-means与CNN的遥感影像分类 一、引言 1.研究背景 航天遥感技术是一种通过卫星对地观测获取遥感图像信息数据的技术,这些图像数据在各领域都发挥着不可或缺的作用。遥感图像分类主要是根据地面物体电磁波辐射在遥感图像上的特征,判断识别地面物体的属…

10 大 Mac 数据恢复软件深度评测

对于任何依赖计算机获取重要文件(无论是个人照片还是重要商业文档)的人来说,数据丢失可能是一场噩梦。值得庆幸的是,有多种专门为 Mac 用户提供的数据恢复工具,可以帮助检索丢失或意外删除的文件。在本文中&#xff0c…

基于Python+Selenium+Unittest+PO设计模式

一、什么是PO设计模式(Page Object Model) 1、Page Object是一种设计模式,它主要体现在对界面交互细节的封装上,使测试用例更专注于业务的操作,从而提高测试用例的可维护性。 2、一般PO设计模式有三层 第一层&#x…

【基于NLP的微博情感分析:从数据爬取到情感洞察】

基于NLP的微博情感分析:从数据爬取到情感洞察 背景数据集技术选型功能实现创新点 今天我将分享一个基于NLP的微博情感分析项目,通过Python技术、NLP模型和Flask框架,对微博数据进行清洗、分词、可视化,并利用NLP和贝叶斯进行情感分…

基于Lucene的全文检索系统的实现与应用

文章目录 一、概念二、引入案例1、数据库搜索2、数据分类3、非结构化数据查询方法1) 顺序扫描法(Serial Scanning)2)全文检索(Full-text Search) 4、如何实现全文检索 三、Lucene实现全文检索的流程1、索引和搜索流程图2、创建索引1)获取原始…

Moco框架的搭建使用

一、前言   之前一直听mock,也大致了解mock的作用,但没有具体去了解过如何用工具或框架实现mock,以及也没有考虑过落实mock,因为在实际的工作中,很少会考虑用mock。最近在学java,刚好了解到moco框架是用于…

语言模型GPT与HuggingFace应用

受到计算机视觉领域采用ImageNet对模型进行一次预训练,使得模型可以通过海量图像充分学习如何提取特征,然后再根据任务目标进行模型微调的范式影响,自然语言处理领域基于预训练语言模型的方法也逐渐成为主流。以ELMo为代表的动态词向量模型开…

创建dockerSwarm nfs挂载

创建dockerSwarm nfs挂载 nfs高可用部署(lsyncd两主机双向同步) nfs高可用部署(lsyncd三主机三向同步) 1. 通过 Volume 1.1 创建 Docker Volume 每个 swarm 节点均创建相同名称的 Docker Volume(名称为 nfs120) docker volume create --driver local …

Jupyter notebook修改背景主题

打开Anaconda Prompt,输入以下内容 1. pip install --upgrade jupyterthemes 下载对应背景主题包 出现Successfully installed jupyterthemes-0.20.0 lesscpy-0.15.1时,说明已经下载安装完成 2. jt -l 查看背景主题列表 3. jt -t 主题名称(…

【docker 】centOS 安装docker

官网 docker官网 github源码 卸载旧版本 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine 安装软件包 yum install -y yum-utils \device-mapper-persistent-data…

Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)

目录 一、前言 二、Bean配置信息重用 1.简介 : 2.实例 : 三、关于Bean的创建顺序 1.简介 : 2.实例 : 四、关于Bean的单例和多例 1.简介 : 2.实例 : 五、关于Bean的生命周期 1.简介 : 2.实例 : 六、Bean配置后置处理器 1.简介 : 2.实例 : 七、通过.properties文…

AcWing 93. 递归实现组合型枚举

Every day a AcWing 题目来源:93. 递归实现组合型枚举 解法1:回溯算法 标准的回溯算法模板题。 如果把 n、m 和数组 nums 都设置成全局变量的话,backtracking 回溯函数可以只用一个参数 level。 注意传参时 nums 不能用引用,…