谏知中文网

悟已往之不谏,知来者之可追

PHP 解决 Excel 大数据导入内存溢出

发表于 2021-05-19 4010 次浏览

PHPOffice/PHPExcel官方已经不维护,推荐使用PHPOffice/PhpSpreadsheet

本文使用类库版本:1.10

Composer安装:composer require phpoffice/phpspreadsheet:^1.10

Excel2003版最大行数是65536行

Excel2007开始的版本最大行数是1048576行

本机环境:CentOS7.9、PHP7.4.12、3G内存

运行模式:CLI

测试样本:40万行记录,大小144M

问题1:单次导入内存溢出

解决方法:解除PHP运行时内存限制,ini_set('memory_limit', 0);

问题2:导入时间长

以测试文件为例,内存占用最多达到2.7G

导入文件(不含业务操作)耗时60秒

问题3:循环导入多个文件,内存占用持续升高

解决方法:单文件读取结束及时释放内存

查看当前内存使用情况:

考虑方案:按行读取

CSV是文本文件,用记事本就能打开,可以做到按行读取

XLS 是二进制的文件只能用 EXCEL 才能打开,所以推荐使用CSV格式

参考网址:https://www.oschina.net/question/187489_2163581 

PhpSpreadsheet处理CSV方式:fopen、fgets、fgetcsv,没有实现按指定行数读取

分析:

如果客户机器内存空间小,有几种方案

  •  以时间换空间:

设置指定行数循环读取,

弊端:需要修改PhpSpreadsheet类库源码

  •  以速度换空间:

PhpSpreadsheet在工作表中平均每个单元格使用约1k,因此大型工作簿可以迅速用尽可用内存。单元缓存提供了一种机制,该机制允许PhpSpreadsheet在较小的内存或非内存(例如,在磁盘上,APCu,内存缓存或Redis、Memcache中)中维护单元对象。这使您可以减少大型工作簿的内存使用量,尽管以访问单元数据的速度为代价。

存在弊端,以Redis为例:

自定义缓存必须实现Psr\SimpleCache\CacheInterface接口,yii2-redis类库无法直接使用

  •  代码示例

实现了Redis、File缓存的使用,可参考:composer require inkime/phpspreadsheetcache

PhpSpreadsheet的实现方式:每个单元格记录缓存,以测试文件为例,40万行、14个单元格/行,总共会生成14x40万个缓存文件,耗时太长难以接受

结论:机器内存不足情况下,优先考虑拆开多个CSV文件,其次使用第三方Redis服务器,最次是File缓存写入磁盘