优化读写:百万级数据分批读写实现(无法使用Spring Batch)
增量式检索并写入百万级数据的实现方案
核心思路:分页分批次查询+逐批写入
直接调用findAll()会把100万+数据一次性加载到内存,极易触发内存溢出。改用分页分批次查询,每次固定拉取一批数据(比如1万或10万条),处理完一批就写入文件并释放内存,循环直到所有数据处理完成,从根本上降低内存压力。
具体代码实现
假设你用的是Spring Data JPA(从dataRepository的用法推断),以下是可直接落地的代码:
int batchSize = 10000; // 每次拉取1万条,可根据单条数据大小、内存情况调整 int pageNum = 0; boolean hasMoreData = true; // 用try-with-resources自动关流,避免资源泄漏 try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))) { while (hasMoreData) { // 分页查询当前批次数据 Page<Data> dataPage = dataRepository.findAll(PageRequest.of(pageNum, batchSize)); List<Data> dataList = dataPage.getContent(); // 逐行写入当前批次 for (Data data : dataList) { bw.write(data.toString()); bw.newLine(); // 可选,让每条数据单独占一行,方便后续读取 } // 每批写完立即刷盘,避免数据积压在内存缓冲区 bw.flush(); // 判断是否还有下一页 hasMoreData = dataPage.hasNext(); pageNum++; } } catch (IOException e) { // 按需处理异常,比如打日志或抛业务异常 e.printStackTrace(); }
关键注意事项
- 调整
batchSize:单条数据体积大就调小批次,反之可适当增大,平衡IO频率和内存占用。 - 流的安全关闭:必须用
try-with-resources语法,确保程序异常时文件流也能被正确关闭,避免资源泄漏。 - 大偏移量性能优化:如果用
OFFSET分页,当页码很大时(比如第100页),数据库查询性能会下降。这时可以改用主键范围查询(假设Data有自增主键id),性能更稳定:
对应的Repository需要新增查询方法:long lastId = 0; int batchSize = 10000; boolean hasMoreData = true; try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))) { while (hasMoreData) { // 按主键范围拉取下一批数据 List<Data> dataList = dataRepository.findByIdGreaterThan(lastId, PageRequest.of(0, batchSize)); if (dataList.isEmpty()) { hasMoreData = false; continue; } // 写入当前批次 for (Data data : dataList) { bw.write(data.toString()); bw.newLine(); } bw.flush(); // 更新最后一条数据的ID,作为下一批查询的起始条件 lastId = dataList.get(dataList.size() - 1).getId(); } } catch (IOException e) { e.printStackTrace(); }@Repository public interface DataRepository extends JpaRepository<Data, Long> { List<Data> findByIdGreaterThan(long lastId, Pageable pageable); }
内容的提问来源于stack exchange,提问作者fragilepriCe
相关产品推荐
相关产品推荐

