Spring Batch分区配置容错后出现NonSkippableReadException问题求助
Spring Batch分区处理中的NonSkippableReadException问题排查与解决
我来帮你拆解这个问题——你遇到的NonSkippableReadException其实是Spring Batch的安全机制在发挥作用,咱们先理清楚根因:
异常产生的原因
- Spring Batch默认将读取阶段(read)的异常标记为不可跳过。它认为读取是数据处理的入口,如果随便跳过读取异常,很容易导致数据遗漏、分区逻辑混乱,所以给read阶段加了这个限制。
- 你现在只配置了对
DeadlockLoserDataAccessException的重试,但当读取阶段抛出这个异常、重试5次都失败后,因为没有明确允许跳过这个异常,Spring Batch就会抛出NonSkippableReadException——直白点说就是:“这个读取异常我没法跳过,重试也失败了,只能终止步骤了”。
解决方法
根据你的场景,有两个可行的解决方向:
1. 明确配置允许跳过读取阶段的死锁异常
如果你确认死锁导致的读取失败不会破坏数据完整性(比如死锁是临时的,跳过的记录后续可以补处理,或者其他分区能覆盖),可以在容错配置里添加跳过规则:
Step masterCalculationStep = stepBuilderFactory.get("STEP_1") .<Map<Long, List<CostCalculation>>, List<TempCostCalc>>chunk(1) .reader(reader) .processor(processor) .writer(writer) .faultTolerant() .retryLimit(5) .retry(DeadlockLoserDataAccessException.class) .skip(DeadlockLoserDataAccessException.class) // 允许跳过该异常 .skipLimit(10) // 设置最大跳过次数,避免无限跳过 .build();
注意:一定要根据业务场景合理设置skipLimit,防止因为异常过多导致大量数据被跳过而不自知。
2. 从根源优化,减少读取阶段的死锁
死锁本质是数据库锁竞争,你可以从读取逻辑入手降低概率:
- 检查Reader的SQL:是否使用了不必要的悲观锁?能不能改成乐观锁(比如版本号机制)?
- 分区逻辑优化:确保每个分区的数据范围划分清晰,不同分区的查询不会竞争同一批数据的锁;
- 调整读取参数:如果用的是
JdbcCursorItemReader,可以调小fetch size,减少单次读取的数据量,降低锁持有时间; - 数据库层面优化:比如调整事务隔离级别(如果业务允许),或者优化表索引,减少锁冲突的概率。
另外补充一点:如果你的Reader是自定义实现,要确保异常被正确抛出到Spring Batch框架,不要在Reader内部捕获后吞掉,否则框架无法感知到异常进行重试或跳过处理。
内容的提问来源于stack exchange,提问作者Shrikant Arbat




