Excel海量数据导入mongodb
我的需求
- 导入Excel的数据,供系统内查询(将近30w行)
- 这个Excel是一个全量的数据,不是增量,系统外维护,有变化了,定期导入更新
WorkbookFactory.create(file.getInputStream());
我的excel不过30M左右,程序占了3g内存,居然还会OOM
参考这个问题:
https://stackoverflow.com/que...
使用这个库,支持用流的方式读取excel:
https://github.com/monitorjbl...
依赖
引用仓库后,出现了Method Not Found的异常,可能是依赖冲突了,我主动排除了一下
com.monitorjbl
xlsx-streamer
2.1.0
org.apache.poi
poi
org.apache.poi
poi
4.1.2
使用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 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 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秒左右,
多提供些内存,多花点时间,百万数据应该也没有什么问题
推荐阅读
- Docker应用:容器间通信与Mariadb数据库主从复制
- 使用协程爬取网页,计算网页数据大小
- Java|Java基础——数组
- Python数据分析(一)(Matplotlib使用)
- Jsr303做前端数据校验
- Spark|Spark 数据倾斜及其解决方案
- 数据库设计与优化
- 爬虫数据处理HTML转义字符
- 数据库总结语句
- MySql数据库备份与恢复