You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Batch中高效处理S3存储的2GB级大CSV文件

解决方案

问题根因

你当前的实现直接用InputStreamResource包装S3流,该Resource没有实现可重读接口,且无法返回准确的内容长度,Spring Batch的FlatFileItemReader为了支持任务重启回溯,默认会缓存流内容到内存,所以会把2GB全量加载,导致内存占用过高。

方案1:使用Spring Cloud AWS S3Resource(推荐)

Spring Cloud AWS提供的S3Resource原生支持S3文件分块拉取,无需全量加载到内存,也不需要手动管理流生命周期,是最优方案。
首先引入对应依赖(Maven示例):

<dependency>
    <groupId>io.awspring.cloud</groupId>
    <artifactId>spring-cloud-aws-starter-s3</artifactId>
    <version>3.1.0</version>
</dependency>

改造后的Reader代码:

@Bean
public FlatFileItemReader<Employee> reader() {
    // 直接构造S3Resource,无需手动获取流,底层自动分块拉取
    S3Resource s3Resource = new S3Resource("s3://testBucket/employee.csv");
    FlatFileItemReader<Employee> reader = new FlatFileItemReader<>();
    reader.setResource(s3Resource);
    reader.setLinesToSkip(1);
    // 增大缓冲区到128KB,减少S3请求次数
    reader.setBufferSize(1024 * 128);
    // 不需要任务重启能力可关闭saveState,减少额外开销
    // reader.setSaveState(false);
    reader.setLineMapper(new DefaultLineMapper() {
        {
            setLineTokenizer(new DelimitedLineTokenizer() {
                {
                    setNames(Employee.fields());
                }
            });
            setFieldSetMapper(new BeanWrapperFieldSetMapper<Employee>() {
                {
                    setTargetType(Employee.class);
                }
            });
        }
    });
    return reader;
}

方案2:临时文件中转(无额外依赖)

如果不想引入Spring Cloud AWS依赖,可以先把S3文件分块写入本地临时文件,再用FileSystemResource传给FlatFileItemReader,同样不会全量占内存:

@Bean
public FlatFileItemReader<Employee> reader() throws IOException {
    S3ObjectInputStream inputStream = getFileFromS3.dowloadFile("employee.csv", "testBucket", "us-east-1");
    // 创建临时文件,JVM退出时自动删除,也可以处理完手动删除
    File tempFile = File.createTempFile("s3-csv-", ".tmp");
    tempFile.deleteOnExit();
    // 分块写入临时文件,单次只占固定缓冲区内存
    try (FileOutputStream out = new FileOutputStream(tempFile)) {
        byte[] buffer = new byte[1024 * 64];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
    } finally {
        inputStream.close();
    }
    FlatFileItemReader<Employee> reader = new FlatFileItemReader<>();
    reader.setResource(new FileSystemResource(tempFile));
    reader.setBufferSize(1024 * 128);
    reader.setLinesToSkip(1);
    // 其余LineMapper配置和原有逻辑一致
    reader.setLineMapper(new DefaultLineMapper() {
        {
            setLineTokenizer(new DelimitedLineTokenizer() {
                {
                    setNames(Employee.fields());
                }
            });
            setFieldSetMapper(new BeanWrapperFieldSetMapper<Employee>() {
                {
                    setTargetType(Employee.class);
                }
            });
        }
    });
    return reader;
}

额外优化建议

  • 调整chunk大小:当前配置chunk=10过小,会导致频繁的事务提交和IO交互,建议调整到100~1000,可根据单条数据的大小灵活调整,内存充足的情况下可以更大。
  • 按需使用S3 Select:如果你的业务只需要处理CSV中部分字段或符合筛选条件的行,可以直接调用S3 Select接口在S3服务端过滤数据,大幅减少需要传输的数据量,性能提升最明显。
  • 多线程处理:如果处理逻辑是CPU密集型,可以开启多线程异步处理Chunk,注意FlatFileItemReader不是线程安全的,多线程场景需要用SynchronizedItemStreamReader包装。

内容的提问来源于stack exchange,提问作者Mike Marsh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 03:45:04