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

Spring Batch字段处理时读取外部文件的性能优化问题

你现在性能差的根本原因是每条主数据处理时都会全量读取遍历一次外部校验文件,假设主文件有10万条、5个校验文件各3万条,你总共会执行10万*5=50万次文件IO、150亿次记录遍历操作,完全是无效开销。

优化方案

1. 预加载所有校验文件到内存缓存(核心优化)

校验文件单文件3万条,全量加载到内存仅占用数MB空间,完全可以在Processor初始化阶段一次性读取解析,存入哈希结构实现O(1)查询,避免重复IO和遍历。
示例修改后的Processor代码:

@Component
public class CustomFileProcesser implements ItemProcessor<Employee, EmpOutput> {
    // 不同校验文件对应的缓存Map,key为匹配用的字段值,value为校验后返回结果
    private Map<String, String> salaryCheckMap;
    // 其他4个字段对应的校验Map同理
    // private Map<String, String> xxxCheckMap;

    // 初始化时一次性加载所有校验文件
    @PostConstruct
    public void loadCheckFiles() {
        salaryCheckMap = loadSalaryFile("C:\\Users\\John\\New\\salary.txt");
        // 依次加载其他4个校验文件
        // xxxCheckMap = loadXxxFile(...);
    }

    @Override
    public EmpOutput process(Employee item) throws Exception {
        EmpOutput emp = new EmpOutput();
        emp.setEmployeeSalary(checkSal(item.getEmployeeSalary()));
        // 其他字段校验同理
        return emp;
    }

    public String checkSal(String sal) {
        // 直接从缓存查,无IO无遍历
        return salaryCheckMap.getOrDefault(sal, "5000");
    }

    // 单独抽离校验文件加载逻辑,也可以复用Spring Batch的FixedLengthTokenizer解析定长文件
    private Map<String, String> loadSalaryFile(String filePath) {
        Map<String, String> map = new HashMap<>();
        File f1 = new File(filePath);
        try (FileReader fr = new FileReader(f1);
             BufferedReader br = new BufferedReader(fr)) {
            String s;
            while ((s = br.readLine()) != null) {
                String value = s.substring(5, 7);
                map.put(value, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}

2. 复用Spring Batch定长解析能力

你已经在主文件读取时用了FixedLengthTokenizer,外部校验文件的解析也可以直接复用该组件,避免自己手写substring索引容易出错的问题,解析逻辑更统一。

3. 开启多线程并行处理(可选,适合主文件数据量极大的场景)

如果主文件数据量超过百万,可以给Step配置多线程执行,进一步提升处理速度,注意缓存Map是只读的,天然线程安全:

@Bean
public Step fileReadingStep() {
    SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
    taskExecutor.setConcurrencyLimit(10); // 配置并发线程数,根据服务器配置调整
    return stepBuilderFactory.get("File-Read-Step1")
            .<Employee,EmpOutput>chunk(1000)
            .reader(itemReader())
            .processor(new CustomFileProcesser())
            .writer(new CustomFileWriter())
            .faultTolerant()
            .skipPolicy(skipPolicy())
            .taskExecutor(taskExecutor) // 开启多线程
            .build();
}

4. 大文件场景兜底优化

如果后续校验文件大小超过内存阈值,可以提前将校验数据导入嵌入式H2数据库并为匹配字段建索引,查询耗时和内存查接近,同时避免占用过多堆内存。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 03:57:02