项目上需要导入一个几百万数据 excel 文件到数据库中,有哪些注意点?
- 标签:后端、场景题
🏛 业务架构师
百万级 Excel 导库,第一原则是:这是数据交付场景,可靠性优先于速度,且不能打挂在线库。我按流程讲关键点。
解析层
- 用 EasyExcel(SAX 流式解析)而不是 POI 全量加载——POI 用户模式读大文件直接 OOM,这是最常见的翻车点
- 逐行校验(格式、必填、枚举值、业务规则),校验和写入解耦
执行模式:异步任务化
- 同步接口接收文件 → 立即返回任务 ID → 后台线程池/MQ 消费解析
- 前端轮询进度(已处理/总行数),百万行要跑几分钟,没有进度反馈的导入在运营眼里就是"卡死了",然后他们会重复点击再导入一次——体验缺陷会转化为数据重复问题
写入层关键点
- 分批插入:每 500~1000 条一批
INSERT ... VALUES (...),(...),配合rewriteBatchedStatements=true - 限速:控制导入 QPS(如每秒 5000 条),观察主从延迟——导入是后台任务,慢一点没关系,把在线业务打挂才是事故
- 走从库/专用实例:初始化类导入直接指向专门通道
失败处理(业务决策点) 两种策略要先和业务方确认,不能工程师自己定:
- 全量事务:一批失败全部回滚——适合初始化数据,要求强一致
- 部分成功 + 错误清单:合法数据入库,失败行导出错误 Excel(含原因)给运营修数——运营日常导入更实用,重跑只处理失败部分,需要导入幂等(业务唯一键 +
INSERT IGNORE或 upsert)防止重跑重复插入
收尾:导入功能的价值在于数据及时进入系统支撑业务,错误清单设计和幂等设计决定了运营是"修几行"还是"全重来",这比写入速度快 3 倍更有业务价值。
🏗 系统架构师
这个问题我踩过坑,核心矛盾是:几百万行 Excel 一次性读进 JVM 必然 OOM,一次性 insert 必然打爆数据库。
解析层:绝不能用 EasyExcel 一次性读的模式,要用 SAX 流式解析(EasyExcel 的 doAfterAllAnalysed 逐行回调、POI 的 XSSFReader 事件模式),内存占用从 GB 级降到 MB 级。同时做好数据校验(手机号格式、必填项),脏数据写错误报告而不是中断全量。
写入层:逐条 insert 一秒几百条,几百万要几个小时,不可接受。我的方案是分档:
- 千万级以下:
rewriteBatchedStatements=true,JDBC batch 每 500~1000 条一批,配合多线程(按批次切片丢给线程池),MySQL 单机 3~5 万行/秒没问题 - 数据量再大或要求快:先流式写成分片文件,走
LOAD DATA INFILE,比 insert 快一个数量级
数据库侧保护:导入前去掉二级索引和外键约束,导完再重建,能快 2~3 倍;导入期间主从延迟会被放大,要避开业务高峰,最好写入从库再校验后切换。如果表是生产热表,直接写一张影子表,导完原子改名,对线上零影响。
过程管理:几百万数据必然跨越多个事务边界,要记录导入进度(游标位点),支持断点续传;导入完做总数校验和抽样比对。入口用分布式锁防止重复触发,异步化执行 + 进度查询接口,别让 HTTP 请求同步等它。
💻 全栈开发专家
几百万行导入,我关心五件事:别 OOM、别锁死、可回溯、可限速、能容错。
1. 流式读,禁止全量加载
用 EasyExcel 的 SAX 监听器逐行回调,内存里永远只有一批:
java
EasyExcel.read(file, RowDTO.class, new AnalysisEventListener<RowDTO>() {
private List<RowDTO> buf = new ArrayList<>(1000);
public void invoke(RowDTO row, AnalysisContext ctx) {
buf.add(row);
if (buf.size() >= 1000) { flush(); }
}
public void doAfterAllAnalysed(AnalysisContext ctx) { flush(); }
}).sheet().doRead();2. 批量写入
- MyBatis batch 模式或手写
INSERT ... VALUES (...),(...)多值插入,每批 500~1000 条,一个事务一批,绝不开大事务。 - 大表导入前先删掉次要索引和触发器,导完再重建,能快几倍。
3. 容错与回溯
- 每行带"文件名 + 行号",失败行单独落错误表(或写错误文件供下载修正后重导),不要一条错整批回滚。
- 记录导入进度(已处理行数)到任务表,中断可续传。
4. 保护数据库
- 主从架构注意主从延迟,导完在从库校验再放行页面查询。
- 通过批量大小 + sleep 或线程池限速,避免把 binlog/IO 打满影响线上。
踩过的坑 Excel 单元格数字精度(订单号被读成科学计数法)一定要按 String 接收再转换;重复导入要靠业务唯一键 ON DUPLICATE KEY UPDATE 或先删后导幂等。