Kafka之日志存储

前言

Kafka中消息是以主题来进行分类,每个主题又有一个或者多个分区,分区数可以再主题创建时或之后增加都可以。每条消息会根据分区规则追加到指定的分区当中。分区中的每条消息都会被分配一个唯一的序列号,也就是通常所说的偏移量 (offset)。

分区文件

考虑多副本的情况,一个分区对应一个日志( Log)。为了防止 Log 过大,Kafka 又引入了日志分段( LogSegment)的概念,将 Log 切分为多个 LogSegment,相当于一个巨型文件被平均分配为多个相对较小的文件。事实上, Log 和 LogSegnient 也不 是纯粹物理意义上的概念, Log 在物理上只以文件夹的形式存储,而每个 LogSegment 对应于磁盘上的一个日志文件和两个索引文件,以及可能的其他文件(比如以“.txnindex”为后缀的事务索引文件〉。
Log对于文件夹(分区),LogSegment对应日志消息及索引文件。
日志关系

向 Log 中追加消息时是顺序写入的,只有最后 一个 LogSegment (activeSegment)才能执行写入操作,在此之前所有的 LogSegment 都不能写入数据。
为了便于消息的检索,每个 LogSegment 中的日志文件(以“ .log”为文件后缀)都有对应的两个索引文件 :偏移量索引文件(以“ .index”为文件后缀〉和时间戳索引文 件(以“ .timeindex” 为文件后缀〉。每个 LogSegment 都有一个基准偏移量 baseOffset,用来表示当前 LogSegment 中第一条消息的offset。 偏移量是一个64位的长整型数,日志文件和两个索引文件都是根据基 准偏移 量( baseOffset)命名的,名称固定为 20 位数字,没有达到的位数则用 0 填充 。 比如第 一个 LogSegment 的基准偏移量为 0,对应的日志文件为 00000000000000000000.logo。

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

v2日志格式

在这里插入图片描述
Record Batch中可以包含多个消息。
length:消息总长度。
attributes:弃用,但是还是在消息格式中占据1B的大小,以备未来的格式扩展。
timestamp delta:时间戳增量。通常一个timestamp需要占用8个字节,如果像这里保存与RecordBatch的其实时间戳的差值的话可以进一步的节省占用的字节数。
offset delta:位移增量。保存与RecordBatch起始位移的差值,可以节省占用的字节数。
headers:这个字段用来支持应用级别的扩展,而不需要像v0和v1版本一样不得不将一些应用级别的属性值嵌入在消息体里面。Header的格式如上图最有,包含key和value,一个Record里面可以包含0至多个Header。
first offset:表示当前RecordBatch的起始位移。
length:计算partition leader epoch到headers之间的长度。
partition leader epoch:用来确保数据可靠性,详细可以参考KIP-101
magic:消息格式的版本号,对于v2版本而言,magic等于2。
attributes:消息属性,注意这里占用了两个字节。低3位表示压缩格式,可以参考v0和v1;第4位表示时间戳类型;第5位表示此RecordBatch是否处于事务中,0表示非事务,1表示事务。 第6位表示是否是Control消息,0表示非Control消息,而1表示是Control消息,Control消息用来支持事务功能。
last offset delta:RecordBatch中最后一个Record的offset与first offset的差值。主要被broker用来确认RecordBatch中Records的组装正确性。
first timestamp:RecordBatch中第一条Record的时间戳。
max timestamp:RecordBatch中最大的时间戳,一般情况下是指最后一个Record的时间戳,和last offset delta的作用一样,用来确保消息组装的正确性。
producer id:用来支持幂等性
producer epoch:和producer id一样,用来支持幂等性
first sequence:和producer id、producer epoch一样,用来支持幂等性
records count:RecordBatch中Record的个数。

日志索引

偏移量索引与日志文件logSegment的对应关系如下图。
在这里插入图片描述

通过偏移量查找指定的消息

例如读取offset=368776的message,需要通过下面2个步骤查找。

第一步查找segment file,其中00000000000000000000.index表示最开始的文件,起始偏移量(offset)为0.第二个文件00000000000000368769.index的消息量起始偏移量为368770 = 368769 + 1.同样,第三个文件00000000000000737337.index的起始偏移量为737338=737337 + 1,其他后续文件依次类推,以起始偏移量命名并排序这些文件,只要根据offset 二分查找文件列表,就可以快速定位到具体文件。 当offset=368776时定位到00000000000000368769.index|log

第二步通过segment file查找message 通过第一步定位到segment file,当offset=368776时,依次定位到00000000000000368769.index的元数据物理位置和00000000000000368769.log的物理偏移地址,然后再通过00000000000000368769.log顺序查找直到offset=368776为止。

segment index file采取稀疏索引存储方式,它减少索引文件大小,通过mmap可以直接内存操作,稀疏索引为数据文件的每个对应message设置一个元数据指针,它比稠密索引节省了更多的存储空间,但查找起来需要消耗更多的时间。

磁盘存储

1.顺序写盘
Kafka 在设计时采用了文件追加的方式来写入消息,即只能在日志文件的尾部追加新的消息,井且也不允许修改己写入的消息,这种方式属于典型的顺序写盘的操作。
在这里插入图片描述
2.页缓存
当一个进程准备读取磁盘上的 文件 内容时,操作系统会先查看待读取的数据所在的页 (page)是否在页缓存( pagecache)中,如果存在(命中〉 则直接返回数据,从而避免了对物 理磁盘的 I/O 操作;如果没有命中,则操作系统会向磁盘发起读取请求并将读取的数据页存入 页缓存,之后再将数据返回给进程 。 同样,如果一个进程需要将数据写入磁盘 ,那么操作系统 也会检测数据对应的页是否在页缓存中,如果不存在, 则会先在页缓存 中添加相应的 页 ,最后 将数据写入对应的页 。被修改过后的页也就变成了脏页,操作系统会在合适的时间把脏页中的数据写入磁盘,以保持数据的 一致性 。

3.零拷贝
零拷贝技术通过 DMA (Direct Memory Access)技术将文件内容复制到内核模式下的 Read Buffer 中 。不过没有数据被复制到 Socket Buffer,相反只有包含数据的位置和长度的信息的文 件描述符被加到 Socket Buffer 中 。 DMA 引擎直接将数据从内核模式中传递到网卡设备(协议 引 擎)。这里数据只经历了 2 次复制就从磁盘中传送出去了, 并且上下文切换也变成了 2 次。 零拷贝是针对 内 核模式而言的 , 数据在内核模式下实现了零拷 贝 。
在这里插入图片描述
非零拷贝流程如下图
在这里插入图片描述

参考文章
Kafka文件存储机制那些事
Kafka消息格式的演变

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值