Excel海量数据导入mongodb

初窥门径 -
Excel海量数据导入mongodb
我的需求导入Excel的数据,供系统内查询(将近30w行)这个Excel是一个全量的数据,不是增量,系统外维护,有变化了,定期导入更新Excel导入

尝试普通的poi导入excel的方式,数据量太大,很容易OOM

WorkbookFactory.create(file.getInputStream());

我的excel不过30M左右,程序占了3g内存,居然还会OOM

参考这个问题:
https://stackoverflow.com/que...

使用这个库,支持用流的方式读取excel:
https://github.com/monitorjbl...

依赖

引用仓库后,出现了Method Not Found的异常,可能是依赖冲突了,我主动排除了一下

<dependency>
            <groupId>com.monitorjbl</groupId>
            <artifactId>xlsx-streamer</artifactId>
            <version>2.1.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
使用xlsx-streamer读取excel:
try (
                InputStream is = file.getInputStream();
                Workbook wb = StreamingReader.builder()
                        // number of rows to keep in memory (defaults to 10)
                        .rowCacheSize(100)
                        // buffer size to use when reading InputStream to file (defaults to 1024)
                        .bufferSize(4096)
                        // InputStream or File for XLSX file (required)
                        .open(is);
        ) {
  ……
}
导入mongodb

数据量比较大,如果组装java对象,肯定浪费性能,我选择直接用 org.bson.Document组装数据。

如果使用 spring-data-mongdb 的API,肯定也有损耗,我选择使用mongdb自己的API:

com.mongodb.client.MongoCollection   
void insertMany(List<? extends TDocument> var1)

来批量导入。维护一个list,从excel中读行组装,每满1000行 insertMany 一次。

我这里是全量导入,不考虑重复更新什么的,直接创建一个新的collection B,导入成功后,drop掉旧的collection A,把collection B重命名为A即可,省心省力。

完整代码:
// 生成一个不会重复的表名
        String collectionName = "invoiceInfo_" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        if (mongoTemplate.collectionExists(collectionName)) {
            mongoTemplate.dropCollection(collectionName);
        }
        // 创建表
        MongoCollection<Document> collection = mongoTemplate.createCollection(collectionName);

        try (
                InputStream is = file.getInputStream();
                Workbook wb = StreamingReader.builder()
                        // number of rows to keep in memory (defaults to 10)
                        .rowCacheSize(100)
                        // buffer size to use when reading InputStream to file (defaults to 1024)
                        .bufferSize(4096)
                        // InputStream or File for XLSX file (required)
                        .open(is);
        ) {
            Sheet sheet = wb.getSheetAt(0);
            long start = System.currentTimeMillis();
            List<Document> documents = new ArrayList<>();
            int i = 0;
            for (Row row : sheet) {
                if (i++ == 0 || row == null) {
                    continue;
                }
                // 从第2行开始取数据,默认第一行是表头.
                int col = 0;

                Document document = new Document();
                Object invoiceCode = ExcelUtil.getCellValue(row, col++);
                if (StringUtils.isEmpty(invoiceCode.toString())) {
                    break;
                }
                document.put("invoiceCode", invoiceCode);
                document.put("invoiceNumber", ExcelUtil.getCellValue(row, col++));
                document.put("billingDate", ExcelUtil.getCellValue(row, col++));
                document.put("buyerName", ExcelUtil.getCellValue(row, col++));
                document.put("buyerTaxId", ExcelUtil.getCellValue(row, col++));
                document.put("sellerName", ExcelUtil.getCellValue(row, col++));
                document.put("sellerTaxPayerId", ExcelUtil.getCellValue(row, col++));
                document.put("noTaxAmount", ExcelUtil.getCellValue(row, col++));
                document.put("taxRate", ExcelUtil.getCellValue(row, col++));
                document.put("taxAmount", ExcelUtil.getCellValue(row, col++));
                document.put("amount", ExcelUtil.getCellValue(row, col++));
                document.put("remark", ExcelUtil.getCellValue(row, col++));
                documents.add(document);

                if (i % 1000 == 0) {
                    collection.insertMany(documents);
                    log.info("已导入{}条", i);
                    documents.clear();
                }

            }
            if (!documents.isEmpty()) {
                collection.insertMany(documents);
                log.info("已导入{}条", i);
            }
            log.info("time = {}", System.currentTimeMillis() - start);

            // 删除旧表
            if (mongoTemplate.collectionExists(TABLE_NAME)) {
                mongoTemplate.dropCollection(TABLE_NAME);
            }
            // 将新表重命名
            collection.renameCollection(new MongoNamespace(mongoTemplate.getDb().getName(), TABLE_NAME));

        } catch (Throwable e) {
            log.error("导入发票信息异常", e);
            mongoTemplate.dropCollection(collectionName);
        }
导入结果

我使用的jvm配置 -Xms512m -Xmx1024m 限制内存最大使用1g

实测30w数据导入用了20~25秒左右,

多提供些内存,多花点时间,百万数据应该也没有什么问题

特别申明:本文内容来源网络,版权归原作者所有,如有侵权请立即与我们联系(cy198701067573@163.com),我们将及时处理。

Tags 标签

excel导入mongodbjava

扩展阅读

加个好友,技术交流

1628738909466805.jpg