
本文介绍在不依赖 Spring Batch 的前提下,通过 JPA 的 PagingAndSortingRepository 实现百万级数据的分页读取与增量文件写入,避免内存溢出,提升系统稳定性与性能。
本文介绍在不依赖 spring batch 的前提下,通过 jpa 的 `pagingandsortingrepository` 实现百万级数据的分页读取与增量文件写入,避免内存溢出,提升系统稳定性与性能。
在处理大规模数据导出(如 100 万+ 条记录)时,一次性加载全部数据到内存(如调用 dataRepository.findAll())极易引发 OutOfMemoryError,同时阻塞 JVM、拖慢响应并影响服务可用性。理想方案是流式分页读取 + 增量写入——即按固定批次(如每批 10 万条)从数据库拉取数据,立即写入文件缓冲区,全程保持低内存占用。
JPA 提供的 PagingAndSortingRepository 接口天然支持分页查询。只需让您的 Repository 继承该接口,并配合 PageRequest 即可实现可控分页:
// 确保 Repository 扩展 PagingAndSortingRepository
public interface DataRepository extends PagingAndSortingRepository<Data, Long> {
}以下是完整、健壮的实现示例(含资源管理与异常处理):
public void exportDataToFile(String filePath) throws IOException {
try (FileWriter fw = new FileWriter(filePath);
BufferedWriter bw = new BufferedWriter(fw)) {
int pageSize = 100_000;
PageRequest pageRequest = PageRequest.of(0, pageSize);
do {
Page<Data> page = dataRepository.findAll(pageRequest);
for (Data data : page.getContent()) {
bw.write(data.toString());
bw.newLine(); // 建议显式换行,确保格式清晰
}
pageRequest = pageRequest.next();
} while (page.hasNext());
bw.flush(); // 显式刷新缓冲区,确保数据落盘
}
}✅ 关键要点说明:
- 使用 try-with-resources 自动关闭 FileWriter 和 BufferedWriter,防止文件句柄泄漏;
- Page 而非 Slice 更推荐——它提供准确的总数(getTotalElements())和分页元信息,便于监控进度;
- 每次循环仅加载当前页数据(page.getContent()),内存峰值 ≈ 单页对象大小,与总数据量无关;
- page.hasNext() 安全判断是否还有下一页,底层由数据库 OFFSET/LIMIT 或游标优化实现(取决于数据库与配置);
- 避免在循环内创建新 BufferedWriter,复用同一实例以减少 I/O 开销。
⚠️ 注意事项:
- 若实体字段含特殊字符(如换行符、逗号),直接 toString() 可能破坏文件结构,建议改用 JSON 序列化(如 Jackson)或 CSV 格式化工具(如 OpenCSV);
- 对于超大数据集(如千万级),考虑启用数据库游标分页(如 PostgreSQL CURSOR 或 MySQL READ UNCOMMITTED + ORDER BY id)以规避 OFFSET 性能退化;
- 生产环境建议添加日志记录每页处理数量及耗时,便于问题追踪与性能调优。
通过该方案,您可在零依赖 Spring Batch 的前提下,安全、高效、可监控地完成海量数据导出任务。

















