【杂谈】Kafka的日志段为什么不用内存映射?

news/2025/1/1 9:34:39/文章来源:https://www.cnblogs.com/longfurcat/p/18637874

什么是内存映射(Memory-Mapped File)?

内存映射(mmap)是一种将文件内容映射到内存中的技术,应用程序可以像操作内存一样对文件内容进行读写,而不需要显式地进行磁盘 I/O 操作。修改的内容会自动由操作系统同步到磁盘。

内存映射需要读取磁盘文件吗?

需要。毕竟,内存中的数据来源于磁盘文件。操作系统会将文件的部分或全部内容加载到内存,供程序访问。

为什么不直接读取文件?

直接读取文件,缓存到用户进程内,这样不也可以随意访问吗?相比这种方式,mmap有何优势?

1. 数据拷贝次数少

内存映射相比直接读取文件的一个主要优势是减少了数据拷贝的次数

  • 内存映射:磁盘 => 内核空间
  • 直接读取:磁盘 => 内核空间 => 用户空间

正常情况下,应用程序不能直接访问内核空间中的数据。要访问这些数据,通常需要触发系统调用将数据从内核空间拷贝到用户空间。

而内存映射通过将文件内容直接映射到进程的虚拟地址空间,消除了这种额外的拷贝开销,从而提高了效率。

2. 加载范围与按需加载

直接读取文件时,通常需要将整个文件加载到进程的内存缓存中,这对于大文件来说非常低效。而内存映射则更加高效,操作系统会根据需要按需加载文件的部分内容。

对于用户来说,内存映射的效果是可以像操作内存一样访问文件内容,而无需担心数据加载的问题。

3. 自动写回磁盘

  • 内存映射:修改的内容会自动同步到磁盘,操作系统会处理文件内容的写回。
  • 直接读取:如果是直接读取,文件内容的修改要么全部写回磁盘,要么应用程序需要识别哪些区域发生了变化并单独写回磁盘,这样的管理工作相对繁琐。

Kafka在哪里使用了内存映射?

从源码中可以看到,Kafka 只在索引文件中使用了内存映射(mmap)。内存映射的优势在于它允许随机访问,这与索引文件的应用场景非常匹配。

Kafka的索引文件通过二分法查找消息的存储位置,而内存映射的随机访问特性使得这个过程更加高效。

但是看源码可以发现,日志段则没有使用文件映射,而是直接使用FileChannel.write(buffer)写出数据。

//kafka 3.9.0部分源码

LogSegment.java

package org.apache.kafka.storage.internals.log
...public class LogSegment implements Closeable {...private final FileRecords log;.../*** Append the given messages starting with the given offset. Add* an entry to the index if needed.** It is assumed this method is being called from within a lock, it is not thread-safe otherwise.** @param largestOffset The last offset in the message set* @param largestTimestampMs The largest timestamp in the message set.* @param shallowOffsetOfMaxTimestamp The last offset of earliest batch with max timestamp in the messages to append.* @param records The log entries to append.* @throws LogSegmentOffsetOverflowException if the largest offset causes index offset overflow*/public void append(long largestOffset,long largestTimestampMs,long shallowOffsetOfMaxTimestamp,MemoryRecords records) throws IOException {if (records.sizeInBytes() > 0) {LOGGER.trace("Inserting {} bytes at end offset {} at position {} with largest timestamp {} at offset {}",records.sizeInBytes(), largestOffset, log.sizeInBytes(), largestTimestampMs, shallowOffsetOfMaxTimestamp);int physicalPosition = log.sizeInBytes();if (physicalPosition == 0)rollingBasedTimestamp = OptionalLong.of(largestTimestampMs);ensureOffsetInRange(largestOffset);// append the messageslong appendedBytes = log.append(records);LOGGER.trace("Appended {} to {} at end offset {}", appendedBytes, log.file(), largestOffset);// Update the in memory max timestamp and corresponding offset.if (largestTimestampMs > maxTimestampSoFar()) {maxTimestampAndOffsetSoFar = new TimestampOffset(largestTimestampMs, shallowOffsetOfMaxTimestamp);}// append an entry to the index (if needed)
// 稀疏索引,有一定的间隔。可以减少索引量if (bytesSinceLastIndexEntry > indexIntervalBytes) {offsetIndex().append(largestOffset, physicalPosition);timeIndex().maybeAppend(maxTimestampSoFar(), shallowOffsetOfMaxTimestampSoFar());bytesSinceLastIndexEntry = 0;}bytesSinceLastIndexEntry += records.sizeInBytes();}}... }

FileRecords.java

package org.apache.kafka.common.record;...public class FileRecords extends AbstractRecords implements Closeable {...  private final FileChannel channel;....public int append(MemoryRecords records) throws IOException {if (records.sizeInBytes() > Integer.MAX_VALUE - size.get())throw new IllegalArgumentException("Append of size " + records.sizeInBytes() +" bytes is too large for segment with current file position at " + size.get());int written = records.writeFullyTo(channel);size.getAndAdd(written);return written;}...}

MemoryRecords.java

package org.apache.kafka.common.record;
....public class MemoryRecords extends AbstractRecords {...private final ByteBuffer buffer;.../*** Write all records to the given channel (including partial records).* @param channel The channel to write to* @return The number of bytes written* @throws IOException For any IO errors writing to the channel*/public int writeFullyTo(GatheringByteChannel channel) throws IOException {buffer.mark();int written = 0;while (written < sizeInBytes())written += channel.write(buffer);buffer.reset();return written;}....
}

 

为什么日志段不使用内存映射?

按理说,直接读写内存不是更快吗?日志段为什么不使用内存映射。

1. 内存消耗过大

Kafka 每个主题和分区都有多个日志段文件。如果将所有日志段文件都映射到内存中,将消耗大量的内存资源。尤其是在日志数据量非常大的情况下,这种做法会极大增加内存的负担,可能会在内存受限的环境中不可行。

2. 顺序读写已足够高效

连续区域:Kafka 的写入和读取操作通常涉及批量消息,这些消息在磁盘上是按顺序存储的。由于数据在物理存储上是连续的,操作系统可以通过一次磁盘寻道就定位到所需的区域,从而减少寻道时间和开销。

页缓存(Page Cache):操系统的页缓存机制(Page Cache)能够将频繁访问的文件内容缓存到内存中。操作系统也会预读取一部分文件后续内容到缓存中,提高缓存命中的概率,避免频繁从磁盘加载数据。

零拷贝(sendfile):Kafka 的日志文件主要由远端消费者触发读取。由于日志在写入文件的时候都已经处理好了,而且读取也是顺序进行的,故Kafka Broker无需进行额外处理,数据可以直接从磁盘通过 sendfile() 系统调用发送到客户端,从内核直接拷贝到 socket 缓冲区,而不需要先载入到用户空间内存中。

 

总结

内存映射技术通过将文件内容映射到内存,有效避免了多次拷贝和高昂的 I/O 成本,非常适合需要随机访问的场景。然而,对于 Kafka 的日志段文件,顺序写入和读取已经足够高效,因此 Kafka 选择不使用内存映射,而是依赖操作系统的页缓存来提高性能。通过这种设计,Kafka 在内存消耗和 I/O 性能之间实现了良好的平衡。

参考内容

https://stackoverflow.com/questions/2100584/difference-between-sequential-write-and-random-write

https://storedbits.com/sequential-vs-random-data/

https://www.mail-archive.com/users@kafka.apache.org/msg30260.html

https://lists.freebsd.org/pipermail/freebsd-questions/2004-June/050371.html

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【WEB安全】利用shuji还原webpack打包源码

一、前言二、webpack简介三、怎么确定是webpack打包站点呢四、shuji(周氏)配置4.1 安装nodejs环境4.2 安装shuji工具4.3 .js.map文件存放位置4.4 运行shuji 反编译.js.map文件获取源码4.5 代码审计五、实战记录免责声明 本公众号所分享内容仅用于网络安全技术讨论,切勿用于违…

万兴PDF专家绿色便携版

点击上方蓝字关注我 前言 万兴PDF一款非常实用的国产软件,它就像是一个超级工具箱,里面装满了处理PDF文件的各种工具。你可以用它来新建PDF、修改PDF里的内容、把PDF转成其他格式,还能给PDF签名、压缩大小、合并多个PDF,甚至比较两个PDF文件的不同。 这款软件的中文版设计得…

2024 第一届Solar应急响应

Tw0队伍 writeup 一、简介 欢迎有更好的解题思路一起交流学习。最好能来点写自动化一把梭工具的代码思路,哈哈哈! 二、 解题情况三、CTF题 签到题目描述: 本题作为签到题,请给出邮服发件顺序。 Received: from mail.da4s8gag.com ([140.143.207.229]) by newxmmxszc6-1.qq.co…

Python深度学习(第2版)PDF免费下载

流行深度学习框架Keras之父执笔,涵盖Transformer架构等进展,文字生,简单方式解释复杂概念,不用一个数学公式,利用直觉自然入门深度学习适读人群 :想要学习深度学习的学生、职业开发者。 流行深度学习框架Keras之父执笔,涵盖Transformer架构等进展,文字生,简单方式解释…

尝试 vmware 16.0.0 过虚拟化 过xf虚拟机检测

前言最新想在vmware虚拟机上玩xf,网上找了不少教程,于是打算自己尝试下。 如果可以修改成功的话,其价值嘛不可估量。 环境 vmware版本是16.0.0,已安装VMware Tools vmware版本是16.1.2,已安装VMware Tools 虚拟镜像cn_windows_7_professional_with_sp1_vl_build_x64_dvd_u…

【OpenGL ES】GLSL基础语法

1 前言 ​ 本文将介绍 GLSL 中数据类型、数组、结构体、宏、运算符、向量运算、矩阵运算、函数、流程控制、精度限定符、变量限定符(in、out、inout)、函数参数限定符等内容,另外提供了一个 include 工具,方便多文件管理 glsl 代码,实现代码的精简、复用。 ​ Unity…

[GPT] LangChain : `LLM` 编程框架

概述:LangChain : LLM (大语言模型)编程框架 基本介绍:LangChainLangChain 就是一个 LLM (大语言模型)编程框架你想开发一个基于 LLM 应用,需要什么组件它都有,直接使用就行; 甚至针对常规的应用流程,它利用链(LangChain中Chain的由来)这个概念已经内置标准化方案了。 本…

[Python/GPT/AI] Ollama指南

概述:Ollama 基本介绍: OllamaOllama是一个支持在Windows、Linux和MacOS上本地运行大语言模型的工具。它允许用户非常方便地运行和使用各种大语言模型,比如Qwen模型等。 用户只需一行命令就可以启动大语言模型。主要特点跨平台支持Windows、Linux、MacOS系统。 提供了丰富的模…

空间曲线的线性参数插值

空间曲线的线性参数插值 ​ 在断层曲面拟合的过程中,发现当解释的空间数据点过于稀疏的化,其断层面拟合的效果较差,我们采用空间曲线线性插值加密的算法,增加插值控制点的数量,改善插值的效果。 1.1 问题描述即算法描述 已知空间三维离散折线 \(l=(p_1,p_2,...,p_i…

关于PY打包文件的解包的记录(转载为主)

由于这个很玄学,加之很多人的文章太老了,所以我打算写一篇文档. 首先得会打包,才能解包.故贴上打包文章:Python pyinstaller打包exe最完整教程_python exe-CSDN博客 我没细看,稍微看了下原理,不知道对不对.1 简介 python提供了多种方法用于将普通的*.py程序文件编译成exe文件(…

C++异常处理机制学习(持续更新)

具体的异常要回去学中断这些,我打算到时候再细致研究,故而这里只是粗浅地讨论C++的异常处理机制.(其实没太看懂原理和应用的关系,以后还要深入研究)首先我们要探究一下seh异常处理机制,从与其相关的数据结构讲起.TIB结构 TIB (Thread Infoimation Block, 线程信息块)是保存线…

PYTHON语言学习笔记(基础语法篇)

Python学习笔记 序言 主要是以小甲鱼的视频为主,https://space.bilibili.com/314076440 一些特性 多次调用方法是从左到右.而参数是函数则先执行参数. 一行如果要多个赋值,用;隔开 input().split() IO 看我放在另一个地方的文档.<D:\Document\md\PYTHON\IO.md> 数据类型 …