- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
内存映射(mmap)是一种将文件内容映射到内存中的技术,应用程序可以像操作内存一样对文件内容进行读写,而不需要显式地进行磁盘 I/O 操作。修改的内容会自动由操作系统同步到磁盘.
需要。毕竟,内存中的数据来源于磁盘文件。操作系统会将文件的部分或全部内容加载到内存,供程序访问.
直接读取文件,缓存到用户进程内,这样不也可以随意访问吗?相比这种方式,mmap有何优势?
1. 数据拷贝次数少 。
内存映射相比直接读取文件的一个主要优势是减少了数据拷贝的次数 。
正常情况下,应用程序不能直接访问内核空间中的数据。要访问这些数据,通常需要触发系统调用将数据从内核空间拷贝到用户空间.
而内存映射通过将文件内容直接映射到进程的虚拟地址空间,消除了这种额外的拷贝开销,从而提高了效率.
2. 加载范围与按需加载 。
直接读取文件时,通常需要将整个文件加载到进程的内存缓存中,这对于大文件来说非常低效。而内存映射则更加高效,操作系统会根据需要按需加载文件的部分内容.
对于用户来说,内存映射的效果是可以像操作内存一样访问文件内容,而无需担心数据加载的问题.
3. 自动写回磁盘 。
从源码中可以看到,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 messages long 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 。
。
最后此篇关于【杂谈】Kafka的日志段为什么不用内存映射?的文章就讲到这里了,如果你想了解更多关于【杂谈】Kafka的日志段为什么不用内存映射?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
什么是内存映射(Memory-Mapped File)? 内存映射(mmap)是一种将文件内容映射到内存中的技术,应用程序可以像操作内存一样对文件内容进行读写,而不需要显式地进行磁盘 I/O 操作。
前言 在分布式消息队列系统中,Kafka 的无锁设计是其高吞吐量和高并发的核心优势之一。通过避免锁的竞争,Kafka 能够在高并发和大规模的生产环境中保持高效的性能。为了更好地理解 Kafka 的无
为什么需要主备结构? 为了确保服务的高可用性,系统不能因为某一个节点的故障而完全不可用。因此,我们需要通过主备结构来确保在主节点发生故障时,备份节点能够迅速接管,继续提供服务。 为什么不直接通过多
为什么要打印日志? 1. 监控系统运行情况 定期查看系统日志是了解服务是否正常运行的重要手段。日志为运维人员提供了实时监控系统状态、发现潜在问题的关键信息。 2. 排查问题(例如异常栈) 日志
背景 接过一个外包的项目,该项目使用JPA作为ORM。 项目中有多个entity带有@version字段 当并发高的时候经常报乐观锁错误OptimisticLocingFailureExcept
前言 在 Kafka 中,消息偏移量是什么?是文件中的索引吗?又是如何通过偏移量快速定位消息的?本文将深入探讨这些问题,帮助你更好地理解 Kafka 的偏移量机制。 Kafka 的偏移量是什么?
一个服务端进程能同时连接多少个 Socket? 要理解一个服务端进程能同时支持多少个连接,首先我们需要明确一个 socket 连接 的表示方式。一个连接由四个部分组成:[LocalIP:LocalP
服务端如何验证客户端已经登录? 在用户成功登录后,服务端会发放一个凭证。之后,客户端的每次请求都需要携带该凭证,服务端通过验证凭证的有效性来判断用户是否已登录,并处理请求。 以下是 Session
1、前言 在6.28/29的稀土掘金开发者大会RAG专场上,我们公司CEO员外代表TorchV分享了我们在《RAG在企业应用中落地的难点与创新》 其中最后分享了两个观点: AI在应用场景落
思维导图 点击下图,可以看大图。 介绍 我把我比较喜欢的和比较关注的地方写下来和大家分享。上次我写了篇《php 跟老大的对话》。还是有很多疑问,这书帮了我不少的忙。&n
思维导图 索引: Ø Move Method(搬移函数) Ø Move Field (搬移值域) &Oslas
思维导图 介绍 承接上文的PHP 杂谈《重构-改善既有代码的设计》之 重新组织你的函数继续重构方面的内容。 这章主要针对数据的重构。
思维导图 点击下图,查看大图。 介绍 条件逻辑有可能十分复杂,因此本章提供一些重构的手法,专门用来简化它们。
思维导图 介绍 前几篇系列文章,我比较关注的是<PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数>,但是我觉得我还是没有说清楚,我自己也有
我是一名优秀的程序员,十分优秀!