文件持续写入场景-性能优化-零拷贝(mmap)高性能文件写入MappedByteBuffer之系列(一)
hello 大家好,我是爱抄中间件代码的路人丙,今天想跟大家分享一下,在公司文件读写业务场景下引入mmap进行文件写入的调研、优化心得体会以及实践。(这篇文章最开始是ppt,是笔者在部门内部的第一次技术分享的内容!)
前言:该篇文章是该系列的第一篇!
笔者之前就看过一部分RocketMQ的源码,所以了解到RocketMQ在文件读写特别是写入使用了mmap,恰好机缘巧合之下,负责公司的业务部分涉及到了文件写入的场景且存在性能问题,于是笔者就想到了RocketMQ的0拷贝文件写入,然后笔者就去扒了RocketMQ对应0拷贝的代码,然后参考其源码对业务代码进行升级改造,最终的结果就是原业务对应接口单次RT的时间缩短了3~15倍(具体的优化效率取决于文件写入的大小,笔者的测试文件大小仅在200M-1.5G范围),最后的产出就是极大的提升了用户对对应功能的使用体验。
注意:
以下涉及0拷贝的内容,均以mmap代替,即描述到mmap时,即指“零拷贝”
看完本系列文章你也将是高性能文件写入的高手:(此篇文章为系列第一篇)
1、mmap是什么
2、Java中的mmap之MappedByteBuffer 安全使用(参考 RocketMQ源码 MappedFile类)
3、代码实操:传统文件写入 vs mmap文件写入的性能对比
4、mmap的优缺点,以及适用场景(参考 RocketMQ源码对mmap的使用,避免写出内存泄漏的代码)
5、高性能文件断点续传的代码
目录
- 文件持续写入场景-性能优化-零拷贝(mmap)高性能文件写入MappedByteBuffer之系列(一)
- 1 什么是零拷贝或者说什么是mmap?
- 2 传统文件写入与使用mmap方式写入,两者之间有什么差异呢?
1 什么是零拷贝或者说什么是mmap?
mmap 是操作系统内核封装的一个系统调用,简单来说他的作用就是:
将文件在磁盘上的位置直接映射到进程虚拟内存区域,那么进程在对该虚拟内存区域进行操作时就好像直接进行了文件的读写操作(当然实际上可能有很多细节,但是这样方便我们具象化理解mmap),不需要调用read和weite函数(虚拟内存技术和零拷贝)
2 传统文件写入与使用mmap方式写入,两者之间有什么差异呢?
首先我们先简单的看一下,传统的文件写入流程:
通过上面的流程图,我们不难发现:
Java使用传统的文件写入过程:
2次上下文切换
2次数据拷贝(CPU、DMA(条件下发生))
如果不考虑脏页回写的情况,单纯对于 JVM 这个进程来说涉及到的性能开销点主要有两次上下文切换,以及一次 CPU 拷贝。其中上下文切换、数据拷贝仍然是主要的性能开销,因为每一次文件写入都会走这个流程
再来一个简化版:
接下来,我们看一下mmap方式(Java版)文件写入方式:
当我们使用mmap时,会调用系统调用mmap(2次上下文切换,创建一个空间虚拟内存与磁盘文件进行映射)
当我们后续进行对应文件写入的时候,直接会写入申请的这块虚拟内存(无需jvm拷贝到内核),DMA拷贝同理传统文件写入(条件发生),所以从理论上来说,mmap文件写入相对传统写入,少了2(n-1)次上下文切换以及n-1次拷贝(n 代表对同一个文件的写入次数,mmap方式只会发起一次系统调用,传统方式每次都会调用系统调用write)
其实从理论层面,有感觉的小伙伴,应该大概感觉mmap的适用场景:对同一个文件多次写入,mmap方式的效率远远会优于传统写入方式,文章的后面笔者会给出2种方式的对比效果!