跳过策略
本章定位:深入理解 Spring Batch 的跳过机制——从声明式
skip().skipLimit()到自定义SkipPolicy,掌握跳过的最佳实践。
定义与作用
跳过(Skip)是 Spring Batch 在遇到预期的、可恢复的数据异常时的处理策略。当一条数据在 Read/Process/Write 阶段失败时,框架可以选择跳过该条数据,继续处理后续数据。
飞翔科技运营部高英提供的 CSV 中偶尔有格式错误的行,小崔用 skip 策略优雅处理:
"CSV 中 5000 条数据有 20 条格式有问题——跳过它们继续处理剩下的 4980 条,总好过因为 20 条错误让整个 Job 失败。"
核心原理
Skip 的内部机制
当启用容错模式后,一个 Chunk 中某条 Item 发生 skip 异常时:
- 框架回滚整个 Chunk 事务
- 以 Chunk Size=1 重新扫描该 Chunk 的所有 Item
- 定位到失败的那一条,跳过它
- 将其他正常的 Item 正常处理并通过 Writer 写出
完整示例
场景一:声明式 Skip 配置
@Bean
public Step skipCapableStep(JobRepository jobRepository,
PlatformTransactionManager tx,
ItemReader<Order> reader,
ItemProcessor<Order, Order> processor,
ItemWriter<Order> writer) {
return new StepBuilder("skipCapableStep", jobRepository)
.<Order, Order>chunk(100, tx)
.reader(reader)
.processor(processor)
.writer(writer)
.faultTolerant()
.skip(FlatFileParseException.class) // CSV 解析错误
.skip(ValidationException.class) // 业务校验失败
.skip(DataIntegrityViolationException.class) // 数据库约束冲突
.skipLimit(100) // 最多跳 100 条
.skipPolicy(new LimitCheckingSkipPolicy(100)) // 等价于上一行
.build();
}
场景二:自定义 SkipPolicy
public class ConditionalSkipPolicy implements SkipPolicy {
@Override
public boolean shouldSkip(Throwable t, long skipCount)
throws SkipLimitExceededException {
// 只跳过特定消息的异常
if (t instanceof ValidationException ve
&& ve.getMessage().contains("DUPLICATE")) {
return true; // 重复数据 → 跳过
}
if (t instanceof FlatFileParseException) {
return skipCount < 50; // CSV 错误最多跳 50 条
}
return false; // 其他异常不跳过
}
}
操作前后对比:
无跳过策略:
Chunk 100 条,第 63 条 CSV 格式错误
→ 整个 Chunk ROLLBACK → Step FAILED → Job FAILED
结果: 5000 条数据全部未处理
有跳过策略(skipLimit=100):
Chunk 100 条,第 63 条 CSV 格式错误
→ 跳过第 63 条,第 1-62, 64-100 条正常写入 → COMMIT
→ 后续 Chunk 继续
结果: 4980 条成功,20 条跳过,Job COMPLETED
易错场景与避坑
反例一:skip 太宽泛——使用 Exception.class
// ❌ 捕获所有异常 → 包括 NPE、OOM 等严重错误!
.faultTolerant()
.skip(Exception.class) // ❌ 太宽泛:连 NullPointerException 都跳过
.skipLimit(100)
正确做法:只跳过预期的、可恢复的异常类型(FlatFileParseException、ValidationException、DataIntegrityViolationException)。
反例二:skipLimit 设置过大
// ❌ skipLimit=10000 但总数据只有 5000 条 → 所有数据都可能被跳过
.skipLimit(10000)
正确做法:skipLimit 应该是一个有意义的阈值。超过这个阈值说明数据质量问题严重,应该让 Job 失败以便人工介入。
面试高频考点
Q1:skip 时框架如何处理已成功的 Item?
框架回滚整个 Chunk,然后以 Chunk Size=1 重新扫描该 Chunk 的每一条 Item。正常 Item 重新处理并写回,失败的那一条被跳过。这保证了:①跳过的只有失败项;②事务一致性;③WriteCount 不包含跳过的项。
Q2:skip 和 retry 的执行顺序?
先 Retry 后 Skip。框架先检查异常是否匹配
retry(),如果匹配且未达retryLimit则重试。重试耗尽后仍失败,再检查是否匹配skip()。如果匹配且未达skipLimit则跳过。