使用内存映射文件MMF实现大数据量导出时的内存优化
前言
导出功能几乎是所有应用系统必不可少功能,今天我们来谈一谈,如何使用内存映射文件MMF进行内存优化,本文重点介绍使用方法,相关原理可以参考文末的连接
实现
我们以单次导出一个excel举例(csv同理),excel包含1~n个sheet,在每个sheet中存储的按行和列的坐标在单元格存储具体数据,如果我们要使用MMF,第一个要考虑的就是如何将整个excel合理的存储到MMF中。这里我们引入MMF两个对象:
MemoryMappedFile --表示内存映射文件
MemoryMappedViewAccessor --表示随机访问的内存映射文件视图
使用MemoryMappedFile.CreateNew(string mapName, long capacity)可以得到一个指定名称和指定大小的内存映射文件,以下简称mmf
* 这里需要注意的是capacity为long类型,以字节为单位,通过计算可知文件大小上限为1G
使用mmf.CreateViewAccessor(long offset, long size)可以得到一个从指定位置开始的指定大小空间的访问器,以下简称accessor
* 这里同样需要注意的是size的大小,如果加上offset超过文件大小,会报System.UnauthorizedAccessException: Access to the path is denied.
考虑到数据体积和管理成本,这里使用mmf对应sheet,使用accessor对应一行数据
* 这里有个需要注意的是mmf不能存储引用类型,包括字符串...,折衷先将string转为char[]然后使用WriteArray方法存储,考虑到取数据的时候同样需要使用char[]数组,而数组必须指定长度,我们将数组长度和具体数据都存起来,这样取数据时候的索引也可以计算出来了
数据存储示例:


这面是具体的实现代码:
//添加外部引用防止被自动GC
public List<MemoryMappedFile> mmfs = new List<MemoryMappedFile>(); /// <summary>
/// 写入
/// </summary>
public void WriteMMF()
{
for(var f = ; f <= ; f ++)
{
//每一个File相当于一个excel的一个sheet(一个患者一行)
var mmf = MemoryMappedFile.CreateNew($"mmftest{f}", * 1024 * 1024); //文件大小最大为1G for (var row = ; row < ; row++)
{
//每一个ViewAccessor相当于excel的一行
var accessor = mmf.CreateViewAccessor(row * * , * ); //通过具体数据量计算空间,offset位移为每个size的长度,size为1M
var index = ;
for (var i = ; i < ; i++)
{
//相当于一行的每一个cell
var buffer = ASCIIEncoding.UTF8.GetBytes($"测试第{row}行第{i}个单元格~!");
var length = buffer.Length;
accessor.Write(index, length);
accessor.WriteArray(index + , buffer, , length);
index += (length + );
}
}
mmfs.Add(mmf);
}
} /// <summary>
/// 读取
/// </summary>
public void ReadMMF()
{
for (var f = ; f <= ; f++)
{
using var mmf = MemoryMappedFile.OpenExisting($"mmftest{f}");
for (var row = ; row < ; row++)
{
using var accessor = mmf.CreateViewAccessor(row * * , * ); //通过写数据同时做的记录控制大小
var index = ;
for (var i = ; i < ; i++)
{
var size = accessor.ReadInt32(index);
var buffer = new byte[size];
accessor.ReadArray(index + , buffer, , size);
var result = ASCIIEncoding.UTF8.GetString(buffer);
Console.WriteLine(result);
index += (size + );
}
}
}
}
运行效果:

* 这里有个需要注意的点是,如果不在外部引用mmf,如果创建多个mmf,只有最新一个能通过OpenExisting方法打开,其他的都报System.IO.FileNotFoundException,猜测是资源被释放掉了
* 还有个需要注意的点是,一定要计算好数据体积,不要超过mmf上限,使用accessor的时候也要注意,数据量实在太大可以考虑将一个sheet拆成多个mmf,或者将一行数据拆成多个accessor
这样就可以实现从数据库获然后处理再存储到载体的流程,整个过程中内存使用控制在一个比较低的水平,当然,这是使用时间换空间,相应的导出时间会延长
顺便说一下,原本考虑后续使用epplus进行excel生成,后来发现npoi也和poi一样有SXSSFWorkbook对象,可以流式读取数据,配合内存映射文件可以实现整个导出过程
相关连接参考:
https://docs.microsoft.com/en-us/dotnet/api/system.io.memorymappedfiles?view=netframework-4.8
https://www.cnblogs.com/flyant/p/4443187.html
https://www.bygeek.cn/2018/05/24/understand-memory-mapped-file/
https://stackoverflow.com/questions/10806518/write-string-data-to-memorymappedfile
使用内存映射文件MMF实现大数据量导出时的内存优化的更多相关文章
- poi 操作Excel 以及大数据量导出
maven 依赖 (版本必须一致,否则使用SXSSFworkbook 时程序会报错) <dependency> <groupId>org.apache.poi</grou ...
- Export大数据量导出和打包
项目需求 导出生成大批量数据的文件,一个Excel中最多存有五十万条数据,查询多余五十万的数据写多个Excel中.导出完成是生成的多个Excel文件打包压缩成zip,而后更新导出记录中的压缩文件路 ...
- 大数据量传输时配置WCF的注意事项
原文:大数据量传输时配置WCF的注意事项 WCF传输数据量的能力受到许多因素的制约,如果程序中出现因需要传输的数据量较大而导致调用WCF服务失败的问题,应注意以下配置: 1.MaxReceivedMe ...
- MySQL大数据量分页查询方法及其优化
MySQL大数据量分页查询方法及其优化 ---方法1: 直接使用数据库提供的SQL语句---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N---适 ...
- java处理大数据量任务时的可用思路--未验证版,具体实现方法有待实践
1.Bloom filter适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集基本原理及要点:对于原理来说很简单,位数组+k个独立hash函数.将hash函数对应的值的位数组置1,查找时如 ...
- 大数据量高并发的数据库优化详解(MSSQL)
转载自:http://www.jb51.net/article/71041.htm 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能. ...
- 大数据量高并发的数据库优化,sql查询优化
一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的. ...
- DB开发之大数据量高并发的数据库优化
一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的编程和维护的难度,而且将会影响系统实际运行的性能.所以,在一个系统开始实施之前,完备的数据库模型的设计是必须的. ...
- SQL Server 使用bcp进行大数据量导出导入
转载:http://www.cnblogs.com/gaizai/archive/2010/04/17/1714389.html SQL Server的导出导入方式有: 在SQL Server中提供了 ...
随机推荐
- html设计时 img与元素存在间距的处理
在学习开发笔书奇小说网站时,遇到一下问题 问题点: 在初始化CSS中设置了img的padding和margin为0,可是在插入img后,img与父元素仍然有一定间距. 原因分析: 文字图片等inlin ...
- 用于数组的delete p324
delete 对象地址; delete 首先调用待清除对象的析构函数,然后释放内存 如果delete一个void指针,唯一发生的事情就是释放了内存.因为通过void指针,无法知道对象的类型,就无法调用 ...
- H5 拖拽元素
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- H3C 单路径网络中环路产生过程(1)
- Linux 内核使用 USB 数据函数
USB 核心中的几个帮忙函数可用来从所有的 USB 设备中存取标准信息. 这些函数不能从 中断上下文或者持有自旋锁时调用. 函数 usb_get_descriptor 获取指定的 USB 描述符从特定 ...
- codeforces 600E E. Lomsat gelral (线段树合并)
codeforces 600E E. Lomsat gelral 传送门:https://codeforces.com/contest/600/problem/E 题意: 给你一颗n个节点的树,树上的 ...
- IdentityServer4 Clients
原文地址 Clients 的定义 Client是指那些从 identityserver获取 token的应用 通常需要为client定义下面通用的设置 唯一的client id secret, 如果需 ...
- 苹果IOS 证书申请及导出证书文件
在申请证书之前,前提是,你要注册一个苹果帐号,且交了个人或公司帐号99美元大概人民币600多一年费用申请开发者帐号,审核通过的开发者帐号,个人或公司或企业类型帐号. 如何申请苹果开发者帐号,这里不讲解 ...
- (转载)window安装mysql
一.MYSQL的安装 1.打开下载的mysql安装文件mysql-5.5.27-win32.zip,双击解压缩,运行“setup.exe”. 2.选择安装类型,有“Typical(默认)”.“Comp ...
- C++Review3_关于C++各种概念的串联与梳理
经过前面两个Review,对代码复用,类的继承概念有了进一步理解. 这里再做一次复盘,把其他概念也串联起来构成一个知识框架. 首先是类和对象的概念.对象指的是特征与技能的结合体,面向对象编程思想的好处 ...