Skip to content

项目上需要导入一个几百万数据 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 或先删后导幂等。