随着技术的不断进步,计算机的速度越来越快。但是磁盘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是一个分布式消息和流平台,具有低延迟、高性能和可靠性、万亿级容量和灵活的可伸缩性。那么问题来了,对于海量消息的处理它是怎么保证高性能和可靠性的呢?

  1. RocketMQ的大致执行流程

RocketMQ中消息生产, 存储和消费流程大致可以分为以下几个流程:

  • 生产者发送消息到Broker「消息中转角色,负责存储,转发消息」
  • Broker中将消息存储在CommitLog中,并在对应的ConsumerQueue中写入消息的commitLogOffset,msgSize,tagCode等信息「消息在CommitLog中的位置,大小,以及标签信息」
  • 消费者从对应的ConsumerQueue中读取到消息的信息,根据消息的位置从CommitLog中读取消息体,然后进行消费

  1. RocketMQ中的Mmap

CommitLog是消息主体以及元数据的存储主体,存储Producer端写入的消息主体内容,消息内容是不定长的。单个CommitLog文件大小是固定的,默认1G ;文件名长度为20位,左边补零,剩余为起始偏移量,比如00000000000000000000代表了第一个文件,起始偏移量为0,文件大小为1G=1073741824;当第一个文件写满了,第二个文件为00000000001073741824,起始偏移量为1073741824,以此类推。消息主要是顺序写入日志文件,当文件满了,写入下一个文件。

消息存储在CommitLog文件中,每个消费者消费消息时,都是根据消息在文件中偏移量, 大小去读取消息。读取消息的过程伴随着随机访问读取,严重影响性能。RocketMQ主要通过Mmap技术对CommitLog文件进行读写,将对文件的操作转化为直接对内存地址进行操作,从而极大地提高了文件的读写效率。


正因为需要使用内存映射机制,故RocketMQ的文件存储都使用定长结构来存储,方便一次将整个文件映射至内存

5.Q&A

  1. Mmap为什么那么快?

使用Mmap对文件的读写操作跨过内核空间,减少1次数据的拷贝,进而提高了文件IO效率。

  1. 相比磁盘空间,内存那么小,Mmap操作是不是很占用内存空间?

需要注意的是,进行Mmap映射时,并不是直接申请与磁盘文件一样大小的内存空间;而是使用进程的地址空间与磁盘文件地址进行映射,当真正的文件读取是当进程发起读或写操作时。

当进行IO操作时,发现用户空间内不存在对应数据页时(缺页),会先到交换缓存空间(swap cache)去读取,如果没有找到再去磁盘加载(调页)。

  1. Mmap有哪些应用场景?

进程间通信:从自身属性来看,Mmap具有提供进程间共享内存及相互通信的能力,各进程可以将自身用户空间映射到同一个文件的同一片区域,通过修改和感知映射区域,达到进程间通信和进程间共享的目的。

大数据高效存取: 对于需要管理或传输大量数据的场景,内存空间往往是够用的,这时可以考虑使用Mmap进行高效的磁盘IO,弥补内存的不足。例如RocketMQ,MangoDB等主流中间件中都用到了Mmap技术;总之,但凡需要用磁盘空间替代内存空间的时候都可以考虑使用Mmap

  1. Mmap有什么缺点?

内存映射文件需要在进程的占用一块很大的连续逻辑地址空间。对于Intel的IA-32的4GB逻辑地址空间,可用的连续地址空间远远小于2---3 GiB。

一旦使用内存关联文件,在程序运行期间,程序的执行可能受关联文件的错误影响。相关联的文件的I/O错误(如可拔出驱动器或光驱被弹出,磁盘满时写操作等)的内存映射文件会向应用程序报异常;而通常的内存操作是无需考虑这些异常的。

有内存管理单元(MMU)才支持内存映射文件。


img

高效IO解决方案-Mmap「给你想要的快」的更多相关文章

  1. 如何评价苹果中国官网 iOS 8 介绍页面的文案「开发者的大事、大快所有人心的大好事」?[转自知乎]

    在什么是「苹果式中文」答案中,小七得出了这个结论: 「苹果式中文」是指句子结构破碎,经常缺乏主语,滥用排比,顶真,偏正短语,和不恰当四字词的广告文体. (有关什么是苹果式中文,小七原来贴错地方了TAT ...

  2. 【LOJ6077】「2017 山东一轮集训 Day7」逆序对 生成函数+组合数+DP

    [LOJ6077]「2017 山东一轮集训 Day7」逆序对 题目描述 给定 n,k ,请求出长度为 n的逆序对数恰好为 k 的排列的个数.答案对 109+7 取模. 对于一个长度为 n 的排列 p ...

  3. 「newbee-mall新蜂商城开源啦」GitHub 上最热门的 Spring Boot 项目,我也要做一次靓仔!

    没有一个冬天不可逾越,也没有一个春天不会到来. 介绍一下新蜂商城的近况,同时,新蜂商城 Vue 版本目前也在开发中,在这篇文章里我也向大家公布一下新蜂商城 Vue 版本的开发进度,和大家同步一下,在不 ...

  4. 「newbee-mall新蜂商城开源啦」 前后端分离的 Vue 版本即将开源

    新蜂商城 Vue 版本 2019 年 10 月份我在 GitHub 开源仓库中上传了新蜂商城项目的所有源码,至今已经有小半年的时间了,感兴趣的可以去了解一下这个 Spring Boot 技术栈开发的商 ...

  5. SSH连接时出现「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」解决办法

    用ssh來操控github,沒想到連線時,出現「WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!」,後面還有一大串英文,這時當然要向Google大神求助 ...

  6. 「ZJOI2019」&「十二省联考 2019」题解索引

    「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...

  7. Loj #6069. 「2017 山东一轮集训 Day4」塔

    Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...

  8. Loj #6073.「2017 山东一轮集训 Day5」距离

    Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...

  9. Loj 6068. 「2017 山东一轮集训 Day4」棋盘

    Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...

随机推荐

  1. PHP zip_entry_compressionmethod() 函数

    定义和用法 The zip_entry_compressionmethod() 函数返回 zip 档案项目的压缩方法.高佣联盟 www.cgewang.com 语法 zip_entry_compres ...

  2. PHP zip_entry_compressedsize() 函数

    定义和用法 zip_entry_compressedsize() 函数返回 zip 档案项目的压缩文件尺寸.高佣联盟 www.cgewang.com 语法 zip_entry_compressedsi ...

  3. PHP crc32() 函数

    实例 输出 crc32() 的结果:高佣联盟 www.cgewang.com <?php $str = crc32("Hello World!"); printf(" ...

  4. 程序员面试:C/C++求职者必备 20 道面试题,一道试题一份信心!

    面试真是痛并快乐的一件事,痛在被虐的体无完肤,快乐在可以短时间内积累很多问题,加速学习. 在我们准备面试的时候,遇到的面试题有难有易,不能因为容易,我们就轻视,更不能因为难,我们就放弃.我们面对高薪就 ...

  5. 星屑幻想 optimal mark

    LINK :SP839 星屑幻想 取自 OJ 的名称 小事情...题目大意还是要说的这道题比较有意思,想了一段时间. 给你一张图 这张图给答案带来的贡献是每条边上两个点值得异或 一些点的值已经被确定 ...

  6. C++类、函数、指针

    1.初始化所有指针. 2. (1)指向常量的指针: (2)常量指针:指针本身为常量: 3.若循环体内部包含有向vector对象添加元素的语句,则不能使用范围for循环. 4.字符数组要注意字符串字面值 ...

  7. 强大的输入框-应用快速启动uTools

    uTools uTools是一个 极简.插件化.跨平台 的现代桌面软件.通过自由选配丰富的插件,打造你得心应手的工具集合. 当你熟悉它后,能够为你节约大量时间,让你可以更加专注地改变世界. uTool ...

  8. java List接口一

    一 List接口概述 查阅API,看List的介绍.有序的 collection(也称为序列).此接口的用户可以对列表中每个元素的 插入位置进行精确地控制.用户可以根据元素的整数索引(在列表中的位置) ...

  9. C#LeetCode刷题-深度优先搜索

    深度优先搜索篇 # 题名 刷题 通过率 难度 98 验证二叉搜索树   22.2% 中等 99 恢复二叉搜索树   45.1% 困难 100 相同的树   48.1% 简单 101 对称二叉树   4 ...

  10. C#LeetCode刷题之#283-移动零(Move Zeroes)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3907 访问. 给定一个数组 nums,编写一个函数将所有 0 移 ...