高效IO解决方案-Mmap「给你想要的快」
随着技术的不断进步,计算机的速度越来越快。但是磁盘IO速度往往让欲哭无泪,和内存中的读取速度有着指数级的差距;然而由于互联网的普及,网民数量不断增加,对系统的性能带来了巨大的挑战,系统性能往往是无数技术人不断追求的方向。
CPU,内存,IO三者之间速度差异很大。对于高并发,低延迟的系统来说,磁盘IO往往最先成为系统的瓶颈;为了减少其影响,往往会引入缓存来提升性能。但是由于内存空间有限,往往只能保存部分数据;并且数据需要持久化,所以磁盘IO仍然不可避免。
无论是从HDD(机械硬盘)到SSD(固态硬盘)的硬件提升;还是从BIO(阻塞IO)到 NIO(非阻塞IO)的软件上的提升;都使得磁盘IO效率得到了很大的提升,但是相比内存读取速度仍然有着接近巨大的差距。今天笔者将介绍一种更加高效的IO解决方案Mmap(内存映射文件,memory mapped file)
1. 用户态和内核态
为了安全,操作系统将虚拟内存划分为两个模块,即用户态和内核态。它们之间是相互隔离的,即使用户程序崩溃了也不会影响系统的运行。

用户态和内核态包含很多复杂的概念,在此不做过多介绍。简单来说,用户态是用户程序代码运行的地方,而内核态则是所有进程共享的空间。所以,当进行数据读写操作时,往往需要进行用户空间和内核空间的交互。
传统的IO模型进行磁盘数据读写时,一般大致需要2个步骤,拿写入数据为例:1.从用户空间拷贝到内核空间;2.从内核空间写入磁盘。

2. Mmap是什么
Mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系.
对文件进行Mmap后,会在进程的虚拟内存分配地址空间,创建与磁盘的映射关系。 实现这样的映射后,就可以以指针的方式读写操作映射的虚拟内存,系统则会自动回写磁盘;相反,内核空间对这段区域的修改也直接反映到用户空间,从而可以实现不同进程间的数据共享。与传统IO模式相比,减少了一次用户态copy到内核态的操作。

3. 性能测试
从实现原理上来看,我们可以大胆预测,Mmap的性能应该是优于传统IO。为了尽可能保证的数据的确性,笔者使用JMH工具对传统IO与Mmap的读和写进行基准测试。测试代码可到笔者github中获取。
需要注意的是,笔者的测试结果并不严谨,真实的差距要比以下结果要明显的多;原因在于,测试方法运行时间包含了文件的创建,内容初始化以及删除操作所需要的时间。以下是笔者电脑的测试结果「系统:macOS 处理器:2.6GHz 六核 i7 内存:16G 磁盘类型:SSD」
随机读性能测试:

随机写性能测试:

从读和写的结果报告中都不难看出,无论是读和写的结果印证了我们的猜想以及理论依据,Mmap的性能要远优于传统IO,而在Java中传统IO中的NIO又优于BIO。
4.Mmap在RocketMQ中的应用
RocketMQ是一个分布式消息和流平台,具有低延迟、高性能和可靠性、万亿级容量和灵活的可伸缩性。那么问题来了,对于海量消息的处理它是怎么保证高性能和可靠性的呢?
- RocketMQ的大致执行流程
RocketMQ中消息生产, 存储和消费流程大致可以分为以下几个流程:
- 生产者发送消息到Broker「消息中转角色,负责存储,转发消息」
- Broker中将消息存储在CommitLog中,并在对应的ConsumerQueue中写入消息的commitLogOffset,msgSize,tagCode等信息「消息在CommitLog中的位置,大小,以及标签信息」
- 消费者从对应的ConsumerQueue中读取到消息的信息,根据消息的位置从CommitLog中读取消息体,然后进行消费

- RocketMQ中的Mmap
CommitLog是消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容是不定长的。单个CommitLog文件大小是固定的,默认1G ;文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件。
消息存储在CommitLog文件中,每个消费者消费消息时,都是根据消息在文件中偏移量, 大小去读取消息。读取消息的过程伴随着随机访问读取,严重影响性能。RocketMQ主要通过Mmap技术对CommitLog文件进行读写,将对文件的操作转化为直接对内存地址进行操作,从而极大地提高了文件的读写效率。

正因为需要使用内存映射机制,故RocketMQ的文件存储都使用定长结构来存储,方便一次将整个文件映射至内存。
5.Q&A
- Mmap为什么那么快?
使用Mmap对文件的读写操作跨过内核空间,减少1次数据的拷贝,进而提高了文件IO效率。
- 相比磁盘空间,内存那么小,Mmap操作是不是很占用内存空间?
需要注意的是,进行Mmap映射时,并不是直接申请与磁盘文件一样大小的内存空间;而是使用进程的地址空间与磁盘文件地址进行映射,当真正的文件读取是当进程发起读或写操作时。
当进行IO操作时,发现用户空间内不存在对应数据页时(缺页),会先到交换缓存空间(swap cache)去读取,如果没有找到再去磁盘加载(调页)。
- Mmap有哪些应用场景?
进程间通信:从自身属性来看,Mmap具有提供进程间共享内存及相互通信的能力,各进程可以将自身用户空间映射到同一个文件的同一片区域,通过修改和感知映射区域,达到进程间通信和进程间共享的目的。
大数据高效存取: 对于需要管理或传输大量数据的场景,内存空间往往是够用的,这时可以考虑使用Mmap进行高效的磁盘IO,弥补内存的不足。例如RocketMQ,MangoDB等主流中间件中都用到了Mmap技术;总之,但凡需要用磁盘空间替代内存空间的时候都可以考虑使用Mmap。
- Mmap有什么缺点?
内存映射文件需要在进程的占用一块很大的连续逻辑地址空间。对于Intel的IA-32的4GB逻辑地址空间,可用的连续地址空间远远小于2---3 GiB。
一旦使用内存关联文件,在程序运行期间,程序的执行可能受关联文件的错误影响。相关联的文件的I/O错误(如可拔出驱动器或光驱被弹出,磁盘满时写操作等)的内存映射文件会向应用程序报异常;而通常的内存操作是无需考虑这些异常的。
有内存管理单元(MMU)才支持内存映射文件。

高效IO解决方案-Mmap「给你想要的快」的更多相关文章
- 如何评价苹果中国官网 iOS 8 介绍页面的文案「开发者的大事、大快所有人心的大好事」?[转自知乎]
在什么是「苹果式中文」答案中,小七得出了这个结论: 「苹果式中文」是指句子结构破碎,经常缺乏主语,滥用排比,顶真,偏正短语,和不恰当四字词的广告文体. (有关什么是苹果式中文,小七原来贴错地方了TAT ...
- 【LOJ6077】「2017 山东一轮集训 Day7」逆序对 生成函数+组合数+DP
[LOJ6077]「2017 山东一轮集训 Day7」逆序对 题目描述 给定 n,k ,请求出长度为 n的逆序对数恰好为 k 的排列的个数.答案对 109+7 取模. 对于一个长度为 n 的排列 p ...
- 「newbee-mall新蜂商城开源啦」GitHub 上最热门的 Spring Boot 项目,我也要做一次靓仔!
没有一个冬天不可逾越,也没有一个春天不会到来. 介绍一下新蜂商城的近况,同时,新蜂商城 Vue 版本目前也在开发中,在这篇文章里我也向大家公布一下新蜂商城 Vue 版本的开发进度,和大家同步一下,在不 ...
- 「newbee-mall新蜂商城开源啦」 前后端分离的 Vue 版本即将开源
新蜂商城 Vue 版本 2019 年 10 月份我在 GitHub 开源仓库中上传了新蜂商城项目的所有源码,至今已经有小半年的时间了,感兴趣的可以去了解一下这个 Spring Boot 技术栈开发的商 ...
- SSH连接时出现「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」解决办法
用ssh來操控github,沒想到連線時,出現「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」,後面還有一大串英文,這時當然要向Google大神求助 ...
- 「ZJOI2019」&「十二省联考 2019」题解索引
「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...
- Loj #6069. 「2017 山东一轮集训 Day4」塔
Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...
- Loj #6073.「2017 山东一轮集训 Day5」距离
Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...
- Loj 6068. 「2017 山东一轮集训 Day4」棋盘
Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...
随机推荐
- PHP str_getcsv() 函数
定义和用法 str_getcsv() 函数解析 CSV 格式字段的字符串,并返回一个包含所读取字段的数组. 语法 str_getcsv(string,separator,enclosure,escap ...
- Skill 脚本演示 ycAutoSnap.skl
https://www.cnblogs.com/yeungchie/ ycAutoSnap.skl 版图编辑中自动吸附 Path 的 "垂直线头",也可以批量对齐 Bus 走线,也 ...
- NOI Online#1 小记
虽然只是一个普通的模拟赛,但是毕竟是我第一次参加官方组织的比赛,所以还是写一篇小记纪念一下吧(毕竟经验少,太菜了. 上午一直颓着,随便看了两眼文化课,补了补昨天的化学作业,就当是对明天月考的复习吧(月 ...
- 吴太银:华为消费者云服务Cassandra使用场景与最佳实践
大家好,我是华为消费者云的吴太银. 我今天分享的主要是华为消费者云服务使用Cassandra的应用场景和最佳实践.我这个可能跟其他嘉宾分享的不太一样,因为前几个嘉宾讲的实际上对Cassandra原生的 ...
- 【译】10 款国外实用、有趣的 GitHub 简介 README
本文翻译自 dev.to 文章<10 Standout GitHub Profile READMEs> 原文链接见:https://dev.to/github/10-standout-gi ...
- Java中增强一个类的几种方法
今天有人问我怎么增强一个类的功能.博客刚好没东西,今天就讲讲增强类. 增强的手段有三种类型: 1.继承或者实现接口:特点是被增强对象不能变,增强的内容不能变. 2.装饰着模式:特点是被增强对象可变,但 ...
- maven配置问题
今天配置maven环境,最后总是显示 百度好多方法,都没解决,最后查看了一下maven目录下的mvn.cmd文件发现里面的jdk引用名用的是%JAVA_HOME%..... 看完就发现问题了,自己装了 ...
- C#LeetCode刷题之#67-二进制求和(Add Binary)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3929 访问. 给定两个二进制字符串,返回他们的和(用二进制表示) ...
- 01 Arduino-点亮一盏LED灯
01 硬件连接 图片比较丑 特别说明:一般默认为二极管灯的压降是 2V 均值电流为15ma,所以如果接在5V的电源上面,串联接的电阻值为200欧姆左右,可做适当调整 切记不允许把LED灯直接并联在5 ...
- Qt 信号发射部分 undefined reference to错误
在使用信号与槽很容易发生 undefined reference to 发射信号 ①继承QObject ②添加Q_OBJECT ③执行qmake ④构建 然后就可以运行啦!但是不知道是为什么,悄咪咪 ...