并行 Step 与 Split
本章定位:区分并掌握 Spring Batch 的三种并行策略——Multi-threaded Step、Split 并行流和 Partitioning 分区,理解各自的适用场景和性能特征。
定义与作用
Spring Batch 提供三种并行处理策略,按复杂度和扩展性递增:
| 策略 | 并行粒度 | 适用数据量 | 复杂度 |
|---|---|---|---|
| Multi-threaded Step | Step 内部多线程 | 10 万 - 100 万 | 低 |
| Split 并行流 | 不同 Step 并行 | 逻辑独立的任务 | 低 |
| Partitioning 分区 | 同一 Step 多 Worker | 100 万 - 亿级 | 中 |
飞翔科技架构师白歌的简化:
"Multi-threaded 是让一个工人手脚并用,Split 是让两个工人各干各的,Partitioning 是把一堆活拆开让十个工人一起干。"
核心原理
完整示例
场景一:Multi-threaded Step
@Bean
public Step multiThreadedStep(JobRepository jobRepository,
PlatformTransactionManager tx) {
return new StepBuilder("multiThreadedStep", jobRepository)
.<Order, Order>chunk(100, tx)
.reader(orderReader())
.processor(orderProcessor())
.writer(orderWriter())
.taskExecutor(taskExecutor()) // 多线程
.throttleLimit(5) // 最多 5 个线程
.build();
}
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
return executor;
}
⚠️ 重要:Multi-threaded Step 要求 Reader 和 Writer 是线程安全的。JdbcCursorItemReader 非线程安全,JdbcPagingItemReader 线程安全。
场景二:Split 并行流
@Bean
public Job parallelFlowsJob(JobRepository jobRepository,
Step initStep, Step finalStep,
@Qualifier("dbFlow") Flow dbFlow,
@Qualifier("fileFlow") Flow fileFlow) {
Flow splitFlow = new FlowBuilder<Flow>("splitFlow")
.split(new SimpleAsyncTaskExecutor())
.add(dbFlow, fileFlow)
.build();
return new JobBuilder("parallelFlowsJob", jobRepository)
.start(initStep)
.next(splitFlow)
.next(finalStep)
.end()
.build();
}
@Bean
public Flow dbFlow(Step extractDbStep, Step transformDbStep) {
return new FlowBuilder<Flow>("dbFlow")
.start(extractDbStep)
.next(transformDbStep)
.build();
}
@Bean
public Flow fileFlow(Step extractFileStep, Step transformFileStep) {
return new FlowBuilder<Flow>("fileFlow")
.start(extractFileStep)
.next(transformFileStep)
.build();
}
运行结果:
initStep → COMPLETED
[并行] dbFlow: extractDbStep → transformDbStep → COMPLETED
[并行] fileFlow: extractFileStep → transformFileStep → COMPLETED
finalStep → COMPLETED
易错场景与避坑
反例一:Multi-threaded Step 用非线程安全的 Reader
// ❌ JdbcCursorItemReader 共享同一个 ResultSet → 多线程并发读取 → 数据错乱
@Bean
public JdbcCursorItemReader<Order> reader(DataSource ds) {
return new JdbcCursorItemReaderBuilder<Order>()
.dataSource(ds)
.sql("SELECT * FROM orders")
.build();
}
反例二:Split 中的 Flow 共享可变状态
// ❌ 两个 Flow 写入同一个文件 → 文件内容交叉
// Flow A: FlatFileItemWriter → output.csv
// Flow B: FlatFileItemWriter → output.csv
面试高频考点
Q1:Multi-threaded Step 中 throttleLimit 的作用?
限制并发线程数。
throttleLimit(5)表示最多 5 个线程同时处理。如果任务中数据天然有 10 个分区,设置throttleLimit=5会让 5 个线程轮流处理 10 个分区。
Q2:何时用 Split 而非 Partitioning?
Split 适用于逻辑独立的任务(DB 抽取 + 文件抽取、两张不同业务表的处理)。Partitioning 适用于同一逻辑处理不同数据分区(同一张表的 10 个 ID 范围)。Split 是逻辑级并行,Partitioning 是数据级并行。
上一章:分区详解下一章:SpringBoot 集成