alibaba / easyexcel

快速、简洁、解决大文件内存溢出的java处理Excel工具
https://easyexcel.opensource.alibaba.com
Apache License 2.0
32.69k stars 7.58k forks source link

导入200万数据, 在导入结束的时候 内存和cpu 突然飙升 #3975

Open ipmaqon58 opened 2 months ago

ipmaqon58 commented 2 months ago

建议先去看文档

快速开始常见问题

触发场景描述

导入200万行excel 解析数据的过程中没有问题 在导入结束的时候 cpu和 jvm堆内存突然飙升 100% java服务卡死 后通过加大内存到6G才不会卡死, 代码被删减掉只剩记录数据的代码 没有任何业务代码

触发Bug的代码

       @PostMapping("/impostSalesOrder")
    @ApiOperation(value = "销售订单导入", notes = "销售订单导入")
    @Log(title = "销售订单", businessType = BusinessType.IMPORT)
    public AjaxResult impostSalesOrder(@Valid SalesOrderImportData data) {
        // 开始时间
        long begin = System.currentTimeMillis();
        String itemCode = UUID.randomUUID().toString();
        try (InputStream inputStream = data.getFile().getInputStream()) {
            EasyExcel.read(inputStream, BizEnterpriseSalesOrderImportDto.class,
                    new SalesOrderListener1(bizEnterpriseSalesOrderService, data.getEnterpriseId(), itemCode)).sheet(1).headRowNumber(1).doRead();
        } catch (BaseException e) {
            throw new BaseException(e.getDefaultMessage());
        } catch (Exception e) {
            // 打印错误信息
            e.printStackTrace();
            throw new BaseException("附件解析出错,请检查数据格式!");
        }
        // 结束时间
        long end = System.currentTimeMillis();
        System.out.println("总共耗时:" + (end - begin) / 1000 + "秒");
        return AjaxResult.success();
    }

package me.nb.ecrs.app.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.ListUtils;
import lombok.extern.slf4j.Slf4j;
import me.nb.ecrs.app.dto.excelDataDto.BizEnterpriseSalesOrderImportDto;
import me.nb.ecrs.app.service.BizEnterpriseSalesOrderService;

import java.util.List;

/**
 * @ClassName: SalesOrderListener
 * @Author: LinF
 * @Date: 2024/8/26 14:39
 * @Description:
 */
@Slf4j
public class SalesOrderListener1 extends AnalysisEventListener<BizEnterpriseSalesOrderImportDto> {

    /*成功数据*/
    private List<BizEnterpriseSalesOrderImportDto> successList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    /*单次处理条数*/
    private final static int BATCH_COUNT = 10000;
    private Long total = 0L;

    /*错误条数上限,超过此上限所有数据将导入失败*/

    private BizEnterpriseSalesOrderService bizEnterpriseSalesOrderService;

    private String enterpriseId;

    // 用作数据校验不通过删除已经导入的数据
    private String itemCode;

    public SalesOrderListener1(BizEnterpriseSalesOrderService service, String enterpriseId, String itemCode) {
        this.bizEnterpriseSalesOrderService = service;
        this.enterpriseId = enterpriseId;
        this.itemCode = itemCode;
    }

    public void invoke(BizEnterpriseSalesOrderImportDto dto, AnalysisContext analysisContext) {
        total++;
    }

    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("所有数据解析完成!");
        // 清空列表,释放内存
        log.info("总共导入{}条数据", total + successList.size());
        successList.clear();
        // 触发GC,尽量释放内存
        System.gc();
    }
}

提示的异常或者没有达到的效果

image 在打印出日志后卡死 内存监控和cpu图 image QQ截图20240904170721