故事起因:一个WMS系统,需要定期更新从SAP导入BOM表数据Excel文件,数据比较多,共4万多条,2.7M。在服务器直接打开时直接OutOfMemory(内存溢出),急需优化。
Ray: 好啊好啊,让我来分析一下先...
问题分析
起因说了,WMS系统,需要定期更新从SAP系统导入BOM表数据Excel文件,数据比较多,共4万多条,2.7M。服务器是1核1G内存,后台是java,用的POI处理Excel(xlsx),只要用户一选文件导入就OutOfMemory(内存溢出)。
<img src="https://pic3.zhimg.com/v2-25e7319f9c0531b06e9c3335c2660306_b.png" data-rawwidth="889" data-rawheight="134" class="origin_image zh-lightbox-thumb" width="889" data-original="https://pic3.zhimg.com/v2-25e7319f9c0531b06e9c3335c2660306_r.png">
当时临时解决方案是:用开发员的机器(8G内存)连PROD数据库去导数据,因为内存够大,所以没有爆掉,但是速度非常慢,导一次需要大概十几分钟。
据此判断就是内存问题,理论上加大内存就行啦,但是这个解决方案不是一个适合的方案。虽然解决了OOM问题,速度问题仍旧是个大患。
java问题代码
<img src="https://pic3.zhimg.com/v2-4d7ee06766a8fdf2aa56660d24f60276_b.png" data-rawwidth="671" data-rawheight="323" class="origin_image zh-lightbox-thumb" width="671" data-original="https://pic3.zhimg.com/v2-4d7ee06766a8fdf2aa56660d24f60276_r.png">
XSSFWorkbook一次过装载几万条数据到内存中肯定会有问题,应该是按行或分批放进内存处理,这个才是能一劳永逸解决问题的方案。
寻找答案
在Google中寻找java read big excel file oom,非常快的找到了一些靠谱的答案,都指向了需要用到POI的新API。请不要使用百度(不解释)。
<img src="https://pic1.zhimg.com/v2-98adccc55189dbf480fec634830a8fe8_b.png" data-rawwidth="784" data-rawheight="1055" class="origin_image zh-lightbox-thumb" width="784" data-original="https://pic1.zhimg.com/v2-98adccc55189dbf480fec634830a8fe8_r.png">
点击XSSF进行查看
<img src="https://pic4.zhimg.com/v2-71b8af4ec23d7eb9dd15523593af4fd7_b.png" data-rawwidth="1060" data-rawheight="156" class="origin_image zh-lightbox-thumb" width="1060" data-original="https://pic4.zhimg.com/v2-71b8af4ec23d7eb9dd15523593af4fd7_r.png">
案例测试
<img src="https://pic1.zhimg.com/v2-de28c84ef00b01705943875d9d4d5dc0_b.png" data-rawwidth="1001" data-rawheight="44" class="origin_image zh-lightbox-thumb" width="1001" data-original="https://pic1.zhimg.com/v2-de28c84ef00b01705943875d9d4d5dc0_r.png">
按照官方的例子改造来打开我的BOM excel, 首先我得重现错误,于是把eclipse VM 调整到256M,使用旧代码导入文件。
<img src="https://pic4.zhimg.com/v2-3ee257b688b3cd00b2c7be3608a81493_b.png" data-rawwidth="450" data-rawheight="346" class="origin_image zh-lightbox-thumb" width="450" data-original="https://pic4.zhimg.com/v2-3ee257b688b3cd00b2c7be3608a81493_r.png">
<img src="https://pic4.zhimg.com/v2-a6e6e79f03540fdcd5f58e79342d75f7_b.png" data-rawwidth="1240" data-rawheight="277" class="origin_image zh-lightbox-thumb" width="1240" data-original="https://pic4.zhimg.com/v2-a6e6e79f03540fdcd5f58e79342d75f7_r.png">重现了OOM错误
重现了OOM错误新代码改造, 官方代码地址: svn.apache.org/repos/asf/p…
我改造的地方
<img src="https://pic2.zhimg.com/v2-296a7e79e2b84b77c3d82cd54423f915_b.png" data-rawwidth="648" data-rawheight="483" class="origin_image zh-lightbox-thumb" width="648" data-original="https://pic2.zhimg.com/v2-296a7e79e2b84b77c3d82cd54423f915_r.png">
改造后再次导入,耗时22秒,虽然时间还是有点稍长,但比起以前十几分钟已足够好,证明方案改造OK了!
<img src="https://pic3.zhimg.com/v2-4a4a32bc4a26cb7d6a495cf02080883a_b.png" data-rawwidth="1136" data-rawheight="52" class="origin_image zh-lightbox-thumb" width="1136" data-original="https://pic3.zhimg.com/v2-4a4a32bc4a26cb7d6a495cf02080883a_r.png">
结论
我使用了当前(2017-06)POI最新的3.16版本中的XSSF and SAX (Event API),暂不确定这个Event API是否是把Excel逐行放进内存处理的,但已经保证了在低内存(-Xmx256M)的情况下不会发生OOM错误。
学海无涯,在学习的道路上,你并不孤单,希望本文可以帮助到相关的人,我是物流IT人,刘宇,谢谢,再见。