沉默是金,总会发光
大家好,我是沉默
假设这样一个真实业务场景:
月底,公司要给 50 万名员工发工资。
系统从 CSV 文件读取工资数据,然后批量写入银行系统。
流程大致是:
读取工资数据 → 校验数据 → 调用银行接口 → 写入数据库
问题来了:
如果在处理到 第 490000 条记录 时,突然发现:
银行卡号错误
此时如果系统采用 传统事务处理方式:
BEGIN
处理 50 万条
COMMIT
那么结果会是:
前面 48 万条成功记录也会全部回滚。
整个批处理直接白干。
这在金融、支付、结算等系统中是 绝对不能接受的。
所以企业级系统通常会采用一种特殊处理模式:
Chunk Processing(块级事务)
这正是 Spring Batch 的核心设计。
今天这篇文章,我们就用一个真实案例彻底讲清楚:
Spring Batch 如何处理 50 万数据,并实现部分回滚。
**-**01-
什么是 Spring Batch?
Spring Batch 是 Spring 官方推出的 企业级批处理框架。
它专门解决:
- 批量数据处理
- 数据迁移
- ETL
- 对账系统
- 报表生成
等问题。
它的核心设计理念:
把大数据拆成小块处理。
Spring Batch 的处理结构非常清晰:
Job
└── Step
└── Chunk
├── Reader
├── Processor
└── Writer
可以理解为:
| 组件 | 作用 |
|---|---|
| Job | 一个完整批处理任务 |
| Step | Job中的一个处理步骤 |
| Chunk | 每次处理的数据块 |
| Reader | 读取数据 |
| Processor | 数据处理 |
| Writer | 数据写入 |
如果我们设计一个 50万工资代发系统,典型架构如下:
+------------------+
| Web 控制台 |
| Job监控 / 启停 |
+--------+---------+
|
|
+--------v---------+
| Batch Controller |
| JobLauncher |
+--------+-----------+
|
|
+--------v--------+
| Spring Batch |
| |
| Job -> Step |
| -> Chunk |
| |
+--------+--------+
|
+--------v--------+
| 数据存储层 |
| MySQL / CSV |
+-----------------+
整体流程:
CSV文件
↓
Reader读取
↓
Processor校验
↓
Writer写入数据库
- 02-
为什么需要「部分回滚」?
假设:
工资数据 = 500000 条
如果使用 传统事务模式
BEGIN
处理 500000
COMMIT
只要有 1条数据失败
结果就是:
全部回滚
这显然不合理。
所以 Spring Batch 使用:
Chunk事务
假设我们设置:
chunkSize = 1000
那么处理流程是:
50万数据
│
├─ Chunk1 (1-1000)
├─ Chunk2 (1001-2000)
├─ Chunk3 (2001-3000)
├─ ...
└─ Chunk500
每个 Chunk:
独立事务
流程如下:
读取1000条
↓
处理1000条
↓
写入1000条
↓
提交事务
假设:
Chunk3
2001 - 3000
其中
第2500条数据失败
Spring Batch处理流程:
Chunk3开始
↓
读取1000条
↓
处理
↓
第2500条异常
↓
回滚Chunk3
↓
重新执行
↓
重试3次
↓
仍失败
↓
跳过该记录
↓
提交其余999条
最终结果:
成功:499999
失败:1
这就是 部分回滚机制。
- 03-
实际应用场景
关键配置(Skip + Retry)
核心代码如下:
.faultTolerant()
.skipLimit(100)
.skip(IllegalArgumentException.class)
.retryLimit(3)
.retry(Exception.class)
含义:
| 配置 | 作用 |
|---|---|
| skipLimit | 最多跳过多少条 |
| skip | 哪些异常允许跳过 |
| retryLimit | 失败重试次数 |
| retry | 哪些异常可以重试 |
核心处理流程
完整数据流如下:
CSV文件
│
▼
FlatFileItemReader
│
▼
SalaryPaymentProcessor
│
▼
JdbcBatchItemWriter
│
▼
MySQL
具体步骤:
1 数据读取
FlatFileItemReader
读取 CSV。
2 数据验证
校验:
- 员工ID
- 金额范围
- 银行卡号
示例:
if (item.getAmount().compareTo(MAX_AMOUNT) >0) {
throw new IllegalArgumentException("金额超过限制");
}
3 批量写入
JdbcBatchItemWriter
批量插入数据库。
性能优化
当数据达到:
50万
100万
500万
单线程处理就会变慢。
Spring Batch支持 并行处理。
1 多线程处理
Chunk1 -> Thread1
Chunk2 -> Thread2
Chunk3 -> Thread3
配置:
.taskExecutor(taskExecutor())
.throttleLimit(10)
2 Partition 分区处理
适合:
百万级
千万级
结构:
Master Step
│
├─ Partition1
├─ Partition2
├─ Partition3
└─ Partition4
每个分区:
独立线程
Spring Batch 元数据表
Spring Batch 会自动创建一些表:
| 表名 | 作用 |
|---|---|
| BATCH_JOB_INSTANCE | Job实例 |
| BATCH_JOB_EXECUTION | Job执行记录 |
| BATCH_STEP_EXECUTION | Step执行记录 |
| BATCH_JOB_EXECUTION_PARAMS | Job参数 |
这些表可以实现:
- Job恢复
- Job重启
- 运行统计
Spring Batch在企业里非常常见:
1 工资代发
50000员工
chunk = 1000
处理50个Chunk。
2 银行对账
100万交易
批量对账。
3 报表生成
每天凌晨
生成:
T+1交易报表
**-****04-**总结
常见问题
Job中途失败怎么办?
Spring Batch支持:
Job Restart
可以:
从失败位置继续
如何重新处理失败数据?
只需要:
查询 status = FAILED
修正后重新执行。
如果你需要处理 几十万甚至百万数据,
Spring Batch几乎是最成熟的解决方案。
核心优势:
-
Chunk事务机制
-
部分回滚
-
失败重试
-
跳过策略
-
任务重启
-
并行处理
Spring Batch 的本质,就是把「大事务」拆成「小事务」。
这样即使某条数据失败,也不会影响整个任务。
热门文章
一套能保命的高并发实战指南
架构师必备:用 AI 快速生成架构图
**-****05-**粉丝福利
我这里创建一个程序员成长&副业交流群,
和一群志同道合的小伙伴,一起聚焦自身发展,
可以聊:
技术成长与职业规划,分享路线图、面试经验和效率工具,
探讨多种副业变现路径,从写作课程到私活接单,
主题活动、打卡挑战和项目组队,让志同道合的伙伴互帮互助、共同进步。
如果你对这个特别的群,感兴趣的,
可以加一下, 微信通过后会拉你入群,
但是任何人在群里打任何广告,都会被我T掉。