故事起因:一个WMS系统,需要定期更新从SAP导入BOM表数据Excel文件,数据比较多,共4万多条,2.7M。在服务器直接打开时直接OutOfMemory(内存溢出),急需优化。
Ray: 好啊好啊,让我来分析一下先...
问题分析
起因说了,WMS系统,需要定期更新从SAP系统导入BOM表数据Excel文件,数据比较多,共4万多条,2.7M。服务器是1核1G内存,后台是java,用的POI处理Excel(xlsx),只要用户一选文件导入就OutOfMemory(内存溢出)。
当时临时解决方案是:用开发员的机器(8G内存)连PROD数据库去导数据,因为内存够大,所以没有爆掉,但是速度非常慢,导一次需要大概十几分钟。
据此判断就是内存问题,理论上加大内存就行啦,但是这个解决方案不是一个适合的方案。虽然解决了OOM问题,速度问题仍旧是个大患。
java问题代码
XSSFWorkbook一次过装载几万条数据到内存中肯定会有问题,应该是按行或分批放进内存处理,这个才是能一劳永逸解决问题的方案。
寻找答案
在Google中寻找java read big excel file oom,非常快的找到了一些靠谱的答案,都指向了需要用到POI的新API。请不要使用百度(不解释)。
点击XSSF进行查看
案例测试
按照官方的例子改造来打开我的BOM excel, 首先我得重现错误,于是把eclipse VM 调整到256M,使用旧代码导入文件。
新代码改造, 官方代码地址:https://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/eventusermodel/XLSX2CSV.java
改造后再次导入,耗时22秒,虽然时间还是有点稍长,但比起以前十几分钟已足够好,证明方案改造OK了!
结论
我使用了当前(2017-06)POI最新的3.16版本中的XSSF and SAX (Event API),暂不确定这个Event API是否是把Excel逐行放进内存处理的,但已经保证了在低内存(-Xmx256M)的情况下不会发生OOM错误。
学海无涯,在学习的道路上,你并不孤单,希望本文可以帮助到相关的人,我是物流IT人,刘宇,谢谢,再见。