kafka的log存储解析

这篇具有很好参考价值的文章主要介绍了kafka的log存储解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

kafkalog存储解析——topic的分区partition分段segment以及索引等

引言Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相互独立的。每个topic又可以分成几个不同的partition(每个topic有几个partition是在创建topic时指定

),每个partition存储一部分Message。借用官方的一张图,可以直观地看到topicpartition的关系。

partition是以文件的形式存储在文件系统中,比如,创建了一个名为page_visitstopic,其有5partition,那么在Kafka的数据目录中(由配置文件中的log.dirs指定的)中就有这

5个目录: page_visits-0 page_visits-1page_visits-2page_visits-3page_visits-4,其命名规则为<topic_name>-<partition_id>,里面存储的分别就是这5partition

数据。

接下来,本文将分析partition目录中的文件的存储格式和相关的代码所在的位置。

Partition中的每条Messageoffset来表示它在这个partition中的偏移量,这个offset不是该Messagepartition数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了

partition中的一条Message。因此,可以认为offsetpartitionMessageidpartition中的每条Message包含了以下三个属性:

其中offsetlong型,MessageSizeint32,表示data有多大,datamessage的具体内容。它的格式和Kafka通讯协议中介绍的MessageSet格式是一致。

Partition的数据文件则包含了若干条上述格式的Message,按offset由小到大排列在一起。它的实现类为FileMessageSet,类图如下:

它的主要方法如下:

我们来思考一下,如果一个partition只有一个数据文件会怎么样?

Kafka是如何解决查找效率的的问题呢?有两大法宝:1) 分段 2) 索引。

Kafka解决查询效率的手段之一是将数据文件分段,比如有100Message,它们的offset是从099。假设将数据文件分成5段,第一段为0-19,第二段为20-39,以此类推,每

段放在一个单独的数据文件里面,数据文件以该段中最小的offset命名。这样在查找指定offsetMessage的时候,用二分查找就可以定位到该Message在哪个段中。

数据文件分段使得可以在一个较小的数据文件中查找对应offsetMessage了,但是这依然需要顺序扫描才能找到对应offsetMessage。为了进一步提高查找的效率,Kafka

每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为.index

索引文件中包含若干个索引条目,每个条目表示数据文件中一条Message的索引。索引包含两个部分(均为4个字节的数字),分别为相对offsetposition

index文件中并没有为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将

索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。

Partition的数据文件

offset

MessageSize

data

append: 把给定的ByteBufferMessageSet中的Message写入到这个数据文件中。

searchFor: 从指定的startingPosition开始搜索找到第一个Messageoffset是大于或者等于指定的offset,并返回其在文件中的位置Position。它的实现方式是从

startingPosition开始读取12个字节,分别是当前MessageSetoffsetsize。如果当前offset小于指定的offset,那么将position向后移动LogOverHead+MessageSize(其

LogOverHeadoffset+messagesize,为12个字节)。

read:准确名字应该是slice,它截取其中一部分返回一个新的FileMessageSet。它不保证截取的位置数据的完整性。

sizeInBytes: 表示这个FileMessageSet占有了多少字节的空间。

truncateTo: 把这个文件截断,这个方法不保证截断位置的Message的完整性。

readInto: 从指定的相对位置开始把文件的内容读取到对应的ByteBuffer中。

1. 新数据是添加在文件末尾(调用FileMessageSetappend方法),不论文件数据文件有多大,这个操作永远都是O(1)的。

2. 查找某个offsetMessage(调用FileMessageSetsearchFor方法)是顺序查找的。因此,如果数据文件很大的话,查找的效率就低。

数据文件的分段

为数据文件建索引

相对offset:因为数据文件分段以后,每个数据文件的起始offset不为0,相对offset表示这条Message相对于其所属数据文件中最小的offset的大小。举例,分段后的一个数

据文件的offset是从20开始,那么offset25Messageindex文件中的相对offset就是25-20 = 5。存储相对offset可以减小索引文件占用的空间。

position,表示该条Message在数据文件中的绝对位置。只要打开文件并移动文件指针到这个position就可以读取对应的Message了。在Kafka中,索引文件的实现类为OffsetIndex,它的类图如下:

主要的方法有:

我们以几张图来总结一下Message是如何在Kafka中存储的,以及如何查找指定offsetMessage的。

Message是按照topic来组织,每个topic可以分成多个的partition,比如:有5partition的名为为page_visitstopic的目录结构为:

partition是分段的,每个段叫LogSegment,包括了一个数据文件和一个索引文件,下图是某个partition目录下的文件:

可以看到,这个partition4LogSegment

借用博主@lizhitao博客上的一张图来展示是如何查找Message的。

比如:要查找绝对offset7Message

这套机制是建立在offset是有序的。索引文件被映射到内存中,所以查找的速度还是很快的。

append方法,添加一对offsetpositionindex文件中,这里的offset将会被转成相对的offset

lookup, 用二分查找的方式去查找小于或等于给定offset的最大的那个offset

小结

1. 首先是用二分查找确定它是在哪个LogSegment中,自然是在第一个Segment中。

2. 打开这个Segmentindex文件,也是用二分查找找到offset小于或者等于指定offset的索引条目中最大的那个offset。自然offset6的那个索引是我们要找的,通过索引文

件我们知道offset6Message在数据文件中的位置为9807

3. 打开数据文件,从位置为9807的那个地方开始顺序扫描直到找到offset7的那条Message。一句话,KafkaMessage存储采用了分区(partition),分段(LogSegment)和稀疏索引这几个手段来达到了高效性。

Kafka 将消息以 topic 为单位进行归纳

将向 Kafka topic 发布消息的程序成为 producers.

将预订 topics 并消费消息的程序成为 consumer.

Kafka 以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 broker.

producers 通过网络将消息发送到 Kafka 集群,集群向消费者提供消息

数据传输的事务定义通常有以下三种级别:

1)最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输

2)最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输.

3)精确的一次(Exactly once:不会漏传输也不会重复传输,每个消息都传输被一次而且仅仅被传输一次,这是大家所期望的

1)节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接

2)如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久

producer 直接将数据发送到 broker  leader(主节点),不需要在多个节点进行分发,为了帮助 producer 做到这点,所有的 Kafka 节点都可以及时的告知:哪些节点是活动的,

目标topic 目标分区的 leader 在哪。这样 producer 就可以直接将消息发送到目的地了

Kafaconsumer 消费消息时,向 broker 发出"fetch"请求去消费特定分区的消息,consumer 指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息,

customer 拥有了 offset 的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的

Kafka 最初考虑的问题是,customer 应该从 brokes 拉取消息还是 brokers 将消息推送到

consumer,也就是 pull  push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消息推送到 brokerconsumer  broker 拉取消息

一些消息系统比如 Scribe  ApacheFlume 采用了 push 模式,将消息推送到下游的 consumer。这样做有好处也有坏处:由 broker 决定消息推送的速率,对于不同消费速率的

consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的速率远大于 consumer 消费的速率

时, consumer 恐怕就要崩溃了。最终 Kafka 还是选取了传统的 pull 模式

Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push 模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条

消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull 模式下,consumer 就可以根据自己的

消费能力去决定这些策略

Pull 有个缺点是,如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,

直到新消息到 t 达。为了避免这点,Kafka 有个参数可以让 consumer 阻塞知道新消息到达

(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发

消息由一个固定长度的头部和可变长度的字节数组组成。头部包含了一个版本号和 CRC32

校验码。

·消息长度: 4 bytes (value: 1+4+n)

·版本号: 1 byte

·CRC 校验码: 4 bytes

·具体的消息: n bytes

(1).Kafka  topic 中一个 parition 大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。

(2).通过索引信息可以快速定位 message 和确定 response 的最大大小。

(3).通过 index 元数据全部映射到 memory,可以避免 segment file  IO 磁盘操作。(4).通过索引文件稀疏存储,可以大幅降低 index 文件元数据占用空间大小。

(1).Kafka 持久化日志,这些日志可以被重复读取和无限期保留

(2).Kafka 是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制数据提升容错能力和高可用性

(3).Kafka 支持实时的流式处理 文章来源地址https://www.toymoban.com/news/detail-479686.html

到了这里,关于kafka的log存储解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包