Spring Batch 高级特性与实践详解

Spring Batch高级特性与实践详解

定位:Batch 第 02 篇,讲透重启续跑机制、跳过与重试策略、并行处理模式、监听器与生产工程实践

适用版本:Spring Batch 5.x(Spring Boot 3.x)


目录


一、重启与续跑

1.1 机制

复制代码
执行失败 → 元数据记录了执行到哪个 Step、哪个 Chunk
重启(用同一 JobInstance)→ 从失败的 Step 继续
   已成功的 Chunk 不重复(已提交),从失败位置往后跑

1.2 条件与限制

要点 说明
Step 可重启 需要记录位置(Reader 可定位,如数据库按 id 继续)
已完成 Step 不重跑 默认跳过已 COMPLETED 的 Step
强制重跑 allowStartIfComplete(true) 可让已完成 Step 重新执行
不可恢复的场景 流式读无位置记录、副作用已外部发生------重跑会重复

1.3 重启的前提是幂等

重启意味着部分数据会被重新处理------写操作必须幂等(唯一键去重/状态机),否则重跑产生重复数据。这是批处理设计的第一原则。


二、跳过与重试

2.1 Skip(跳过)

复制代码
指定某类异常时跳过该条,继续处理后续数据(限制跳过总数):
   .faultTolerant()
   .skip(ValidationException.class)
   .skipLimit(100)

用途:脏数据/校验不通过的条目不阻塞整个批处理;超过 skipLimit 才失败。

2.2 Retry(重试)

复制代码
指定某类异常时重试该条(限制次数):
   .retry(DeadlockLoserDataAccessException.class)
   .retryLimit(3)

用途:瞬态故障(死锁、网络抖动)自动重试。

2.3 记录与监听

复制代码
SkipListener:onSkipInRead/onSkipInProcess/onSkipInWrite ------ 记录被跳过的数据

纪律:跳过的数据必须可追溯(落日志/落表),否则"数据少了"无法排查。


三、并行处理

3.1 四种模式

模式 做法 注意
多线程 Step 单 Step 内多线程并行处理(.throttleLimit/线程池) Reader/Processor/Writer 必须线程安全
并行 Step 互不依赖的 Step 用 Flow 同时执行 步骤间无数据依赖
分区(Partitioning) 把大任务按范围拆成子分区,各分区独立并行 分区键要可拆分(如按 id 段)
远程分块 读在主节点,处理分发到多机 跨进程,架构更复杂

3.2 多线程的线程安全要求

复制代码
并行处理时:
   Reader 被多线程调用 → 必须支持并发读或加锁
   Processor 无状态
   Writer 批量写要支持并发提交
不满足 → 数据错乱/丢失

3.3 分区示例思想

复制代码
数据范围 1~1000 万
   → 拆成 10 个分区(1~100万、100万~200万...)
   → 每分区一个独立的处理实例并行跑
   → 汇总等待全部分区完成

适合数据可按键自然切分、各分区无交互的场景。


四、监听器与可观测

4.1 监听器体系

监听器 时机
JobExecutionListener Job 开始/结束
StepExecutionListener Step 开始/结束
ChunkListener 每个 Chunk 前后
ItemReadListener 等 单条读/处理/写前后与异常

4.2 用途

复制代码
① 日志:记录执行开始/结束、耗时、读写计数
② 指标:对接监控(吞吐、失败率、时长)
③ 通知:成功/失败发告警
④ 资源准备/清理:开始前准备、结束后清理

执行元数据(读写计数、提交数、状态)本身已入库,是监控的数据源。


五、工程实践

5.1 幂等设计(最重要)

复制代码
批处理必然重跑(失败重启/补数),写操作必须幂等:
   ① 唯一键:业务唯一索引,重复插入被拒
   ② 状态机:已处理状态不再重复处理
   ③ 去重表/去重键:处理前检查

5.2 调度与编排

复制代码
生产用外部调度平台(而非应用内 @Scheduled):
   依赖编排(A 完成后跑 B)、失败告警、手动重跑、执行日历

5.3 资源隔离

复制代码
批处理对库的压力大:
   ① 错峰:避开在线高峰
   ② 限流:控制并发与批量大小,避免拖垮在线库
   ③ 读从库/独立库:大批量读走从库或专用库

5.4 元数据治理

复制代码
执行历史持续增长 → 定期清理(保留期策略)
关键执行结果归档审计

六、总结

  1. 重启续跑:基于元数据从失败位置继续;前提是 Reader 可定位 + 写操作幂等;已完成 Step 默认不重跑。
  2. 容错:Skip 跳脏数据(限数量 + 必须记录)、Retry 抗瞬态故障(限次数)。
  3. 并行:多线程 Step(线程安全要求)、并行 Step(Flow)、分区(按键拆分)、远程分块;按数据可拆分性选择。
  4. 监听器:Job/Step/Chunk/Item 各级钩子,支撑日志、指标、告警;执行元数据是监控数据源。
  5. 工程纪律:幂等第一、调度平台化、资源隔离防拖垮在线、元数据定期清理。

七、常见高频面试题

1. Spring Batch 如何实现失败后从断点续跑?

要点:基于执行元数据。每个 Chunk 是提交点,执行状态(哪个 Step、哪个 Chunk、读到哪)持久化在元数据表;失败后用同一 JobInstance 重启,框架跳过已成功的部分,从失败的 Step/Chunk 继续。前提:① Step 可重启------Reader 能定位继续位置(如按主键范围);② 写操作幂等------重跑不产生重复。限制:已完成的 Step 默认不重跑(需 allowStartIfComplete 强制)。

2. 为什么批处理必须做幂等?

要点:重跑是批处理的常态------失败重启、人工补数、重复触发都可能让部分数据被再次处理。若写操作不幂等,重跑产生重复数据(重复结算、重复发通知,资损风险)。实现:① 业务唯一键(唯一索引拒绝重复插入);② 状态机(已处理状态跳过);③ 去重表/幂等键检查。幂等是批处理可靠性的第一设计原则,与在线接口的幂等要求同源。

3. Skip 和 Retry 策略怎么用?要注意什么?

要点:Skip------指定某类异常(如校验失败)跳过该条继续处理,配 skipLimit 限制跳过总数,超限才失败;用于脏数据不阻塞批处理。Retry------指定瞬态异常(死锁、网络抖动)重试该条,配 retryLimit;用于抗瞬时故障。注意:① 被跳过的数据必须记录(SkipListener 落日志/表),否则数据缺失无法排查;② 重试的写操作要幂等(重试可能已部分生效);③ 谨慎定义跳过异常类型,别把严重错误也跳过。

4. Spring Batch 有哪些并行处理方式?

要点:四种。① 多线程 Step------单 Step 内多线程并行处理,要求 Reader/Processor/Writer 线程安全;② 并行 Step------用 Flow 让互不依赖的 Step 同时执行;③ 分区(Partitioning)------把大数据按键拆成子分区,各分区独立并行处理后汇总,适合可自然切分的数据;④ 远程分块------读在主节点、处理分发到多台机器(跨进程)。选择:单机内优先多线程/分区,步骤独立用并行 Step,超大规模跨机才用远程分块。

5. 多线程 Step 的线程安全要注意什么?

要点:并行处理时组件被多线程并发调用。Reader------必须支持并发读(线程安全的读取或内部加锁),否则重复读/漏读;Processor------保持无状态,不共享可变状态;Writer------批量写要支持并发提交,数据库操作注意连接与事务边界(每个线程各自的提交)。另外共享的聚合统计要线程安全。不满足会导致数据错乱、丢失、重复------并行化前必须逐一确认线程安全性。

6. 分区(Partition)是怎么工作的?

要点:把大任务按某个键(通常是主键范围或哈希)拆成多个互不重叠的子分区,每个分区分配给一个独立的工作线程/实例并行处理(各自有自己的 Reader 范围),全部完成后汇总。关键:① 分区逻辑要保证不重叠不遗漏;② 各分区数据独立、无交叉依赖;③ 分区数量决定并行度。适合百万~亿级可按键切分的数据。与多线程 Step 的区别:分区是数据切分后各自独立流,多线程是同一流程并发执行。

7. 批处理任务怎么做监控告警?

要点:数据源是执行元数据(状态、读写计数、耗时)+ 监听器钩子。建设:① 执行结果监控------成功/失败、执行时长(超阈值告警)、未按时触发告警;② 数据处理量监控------读写计数异常(为 0 或骤变告警);③ 跳过/失败明细------SkipListener 记录可查;④ 依赖与资源------数据源连接、磁盘(文件型任务)。落地:监听器上报指标到监控平台,调度平台提供失败告警与执行日历。批处理无人值守,监控必须完备。

8. 批处理和在线服务共用数据库,怎么防止互相影响?

要点:批处理大数据量读写会与在线请求争抢数据库资源(连接、IO、锁)。措施:① 时间错峰------批处理安排在在线低峰;② 限流------控制批处理的并发度与批量大小,避免打满连接池;③ 读写分离------批处理的大批量读走从库或专用只读实例,写控制速率;④ 独立连接池------批处理与在线分池,互不抢占;⑤ 必要时独立库/资源组隔离。原则:批处理是"可延时的",在线是"必须保障的",资源分配向在线倾斜。

9. 如何防止批处理任务被并发重复触发?

要点:多层防护。① 框架层------同一 JobInstance(同参数)已在运行/完成时,再次启动被拒绝(元数据表实例唯一性约束);② 调度层------调度平台配置互斥执行(上一次未完成不触发下一次);③ 应用层------分布式场景加分布式锁(防多实例同时触发);④ 参数设计------用唯一参数标识每次运行,避免歧义。核心是"同一逻辑运行只执行一次",与在线接口的幂等防重思想一致。

10. 什么时候该用 Spring Batch,什么时候直接用消息队列?

要点:按数据形态与时效。Spring Batch 适合:有明确数据集(文件/表)、批量加工、需要进度跟踪与断点续跑、离线窗口执行------强调"处理完这批数据"。消息队列适合:持续流入的事件、实时/准实时处理、削峰、解耦------强调"来一条处理一条"。也可组合:队列积压的事件攒成批,用批处理框架定时消费(如 Spark/批任务拉取)。选择标准:批量边界清晰、需重跑与进度管理用 Batch;流式、低延迟用消息。

相关推荐
她说..1 小时前
常见设计模式-观察者模式
java·spring boot·spring·观察者模式·设计模式
vipxieliang1 小时前
Actuator 与 Micrometer:生产级监控体系搭建
java·spring boot
Seven971 小时前
Java已死?社招第一次跳槽:Boss沟通505次,6周拿下8个offer
java
一级新生2 小时前
LLM _ Agent _ Skill _ 专家 的关系与选型笔记
java·开发语言·人工智能·笔记·大模型·智能体·skill
文丑颜不良啊2 小时前
LeetCode1541~1560题目解析
java·leetcode
她说彩礼65万2 小时前
C# TaskCompletionSource
java·开发语言·c#
当战神遇到编程2 小时前
Java 基础入门(七):递归详解——执行原理、方法调用栈与经典练习
java
若亦_2 小时前
排序>>交换排序
java·数据结构·算法
Wang's Blog2 小时前
MongoDB 地理位置上报实战:从接口设计到覆盖写入的完整实现
java·交友