PHP大数据导出EXCEL文件
日期 2021-03-04 15:51:43
阅读 14
phpoffice/phpexcel作为PHP常用导出EXCEL的扩展,因其强大的功能,颇受PHPer的喜爱。
在最近一次数据导出EXCEL的项目中,在线上一运作直接就无法执行,经过一系列的排查发现,公网的数据量大概在10万-20万左右,由于数据量巨大,phpexcel使用的内存超出了php中memory_limit默认128M的限制,达到了200多M,导致运行失败。
因此,首先考虑的问题,增加php的可使用内存,于是设置以下语句:
ini_set("max_execution_time", 0); // 不限执行时间
ini_set('memory_limit', '1024M'); // 设置可使用内存为1024M
以上两句设置,基本解决了运行的问题,于是进行了第一次尝试,结果如下:
执行开始时间:2021-03-02 14:11:05
执行结束时间:2021-03-02 14:13:15
执行时间:130秒
使用内存:192M
导出行数:68560
可以发现,phpoffice/phpexcel是相当消耗性能,而且执行时间也非常长。接着同时打开几个请求地址,结果,由于过多的请求,直接测试网站崩溃,一查原因是cpu满负荷,在请求过程中一直是80%-90%的使用量,又由于执行时间过长,cpu一直被占用,其他进程无法使用,导致服务器崩溃,于是考虑使用phpexcel的缓存机制。
有以下几种缓存方式可以使用:
  • cache_in_memory;
        默认情况下,如果你不初始化任何缓存方式,PHPExcel将使用内存缓存的方式。
  • cache_in_memory_serialized;  
       使用这种缓存方式,单元格会以序列化的方式保存在内存中,这是降低内存使用率性能比较高的一种方案。
  • cache_in_memory_gzip;
与序列化的方式类似,这种方法在序列化之后,又进行gzip压缩之后再放入内存中,这回跟进一步降低内存的使用,但是读取和写入时会有一些慢。
  • cache_to_discISAM;
当使用cache_to_discISAM这种方式时,所有的单元格将会保存在一个临时的磁盘文件中,只把他们的在文件中的位置保存在PHP的内存中,这会比任何一种缓存在内存中的方式都慢,但是能显著的降低内存的使用。临时磁盘文件在脚本运行结束是会自动删除。
  • cache_to_phpTemp;
类似cache_to_discISAM这种方式,使用cache_to_phpTemp时,所有的单元格会还存在php://temp I/O流中,只把他们的位置保存在PHP的内存中。PHP的php://memory包裹器将数据保存在内存中,php://temp的行为类似,但是当存储的数据大小超过内存限制时,会将数据保存在临时文件中,默认的大小是1MB,但是你可以在初始化时修改它
使用方法如下所示:
$cacheMethod = \PHPExcel_CachedObjectStorageFactory::cache_to_discISAM;
$cacheSettings = ['dir' => $path];
\PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);
$PHPExcel = new \PHPExcel(); ......
测试结果如下:
1.cache_in_memory
执行开始时间:2021-03-02 14:18:45
执行结束时间:2021-03-02 14:21:04
执行时间:139秒
使用内存:192M
导出行数:68560
2.cache_in_memory_serialized
执行失败
3.cache_in_memory_gzip
执行开始时间:2021-03-02 14:24:26
执行结束时间:2021-03-02 14:27:25
执行时间:179秒
使用内存:182M
导出行数:68560
4.cache_to_discISAM
执行开始时间:2021-03-02 14:29:14
执行结束时间:2021-03-02 14:32:10
执行时间:176秒
使用内存:279M
导出行数:68560
另外还有memcached等缓存机制,条件有限,请自测。
综上可以看出,性能消耗都很大,而且无法容忍长时间执行cpu爆满。顿时陷入不知所措中,恰在此时,一位老哥(博客)给推荐了一个新扩展rap2hpoutre/fast-excel
测试结果如下:
执行开始时间:2021-03-02 17:50:07
执行结束时间:2021-03-02 17:50:14
执行时间:7秒
使用内存:103M
导出行数:99627
可以看出,内存使用大大降低了,执行时间也非常短了。
如果配合使用生成器
执行开始时间:2021-03-03 08:46:56
执行结束时间:2021-03-03 08:47:06
执行时间:10秒
使用内存:3M
导出行数:99627
 
执行开始时间:2021-03-03 09:03:19
执行结束时间:2021-03-03 09:04:22
执行时间:63秒
使用内存:2M
导出行数:552119
由此可以看出,资源占用非常少,又因为该数据导出一个月只执行一次,所以基本可以满足10万-20万导出而不会过多占用服务器资源的目的。
在此,作为记录,以供日后参阅,同时再次感谢老哥的指导(博客地址)