在批处理系统里,最常见的多数据源诉求是把Spring Batch元数据表和业务数据表分开,避免作业执行记录与业务读写混在一套连接池里相互影响。与此同时,Spring Boot与Spring Batch在默认模式下更偏向只认一个DataSource,如果你直接塞进两套数据源,往往会出现注入歧义、事务管理器选错、元数据表初始化跑偏等问题。Spring Boot官方对这种场景给了明确的解决入口,核心就是区分批处理专用的数据源与事务管理器。
一、Spring Batch如何配置多个数据源
配置多数据源的关键不是定义两套连接那么简单,而是让批处理基础设施知道它应该用哪一套来写入JobRepository元数据,同时让业务读写组件稳定指向业务库。建议先把命名、用途与注入边界定清楚,再落到配置文件与Bean定义里。
1、先把两套数据源的职责写清楚
把批处理数据源定义为只服务于JobRepository与JobExplorer等元数据访问,把业务数据源定义为ItemReader与ItemWriter等业务读写访问,这样后续你在排查连接池与事务问题时有明确归属。
2、在application.yml里分开写两套连接信息
业务数据源沿用spring.datasource的默认前缀,批处理数据源用自定义前缀如batch.datasource或spring.datasource.batch都可以,重点是你后面用 ConfigurationProperties绑定时能一眼区分,避免把账号密码改错库。
3、定义两个DataSource并明确谁是主候选
业务数据源标记为 Primary,批处理数据源在 Bean方法上加名为 BatchDataSource的注解,让Spring Boot批处理自动配置把它当作批处理专用数据源注入。这个注解的语义就是让Batch自动配置优先选它,且通常与另一个被 Primary标记的数据源并存。
4、配两套事务管理器并把批处理那套标出来
业务事务管理器照常作为默认事务管理器使用,批处理事务管理器在 Bean方法上加名为 BatchTransactionManager的注解,让批处理基础设施在建JobRepository时使用它。若你希望两套事务管理器都存在且不互相抢注入候选,按官方建议把 Bean的defaultCandidate设为false用来降低被默认挑中的概率。
5、需要更细控制时改用显式JobRepository配置
当你要强制指定JobRepository使用哪一个dataSource与transactionManager,或要调整表前缀、建表隔离级别等参数,可以用名为 EnableJdbcJobRepository的注解属性来绑定dataSourceRef与transactionManagerRef,并在同一处把tablePrefix与isolationLevelForCreate这类参数统一管理。这样能把批处理基础设施的选型从自动推断改成显式指向。
6、确认你是否还让Spring Boot替你初始化元数据表
如果你通过名为 EnableBatchProcessing的注解或继承DefaultBatchConfiguration来接管配置,Spring Boot会对批处理自动配置回退,其中包含JDBC存储下的元数据表初始化行为,这会影响你首次部署时表到底由谁来建。你的策略要么是由平台侧预建表并纳入数据库变更流程,要么是保留Boot的初始化能力并控制初始化开关与环境。
二、Spring Batch多数据源怎么防止冲突
多数据源冲突的本质是边界不清导致的误用,包括Bean注入歧义、事务管理器串用、元数据表写到业务库、以及并发启动下的元数据竞争。下面这几条按真实故障高频点来做约束,能把大多数冲突提前消掉。
1、所有数据库相关注入都写清楚指向
JdbcTemplate、EntityManagerFactory、MyBatis SqlSessionFactory、读写器构造参数里凡是涉及DataSource的地方,都用 Qualifier明确指定业务数据源或批处理数据源,不要依赖按类型自动注入,否则一旦Bean数量变化就容易注入到错误对象。
2、让批处理元数据写入始终只走批处理数据源
把JobRepository绑定到批处理DataSource与批处理事务管理器,避免出现Step里业务写入回滚了但元数据已提交,或反过来元数据记录失败导致作业状态异常的问题。显式指定transactionManagerRef是防串用的有效手段。
3、同库同schema时用表前缀隔离元数据集合
如果批处理元数据表必须和业务表放在同一个schema,至少要通过tablePrefix做命名隔离,避免与既有表名冲突,也方便你在一个schema里保留多套元数据表集合。Spring Batch明确说明修改表前缀可以应对需要在同一schema内存在多套元数据表或需要加schema前缀的场景,同时Spring Boot也提供spring.batch.jdbc.table-prefix来配置JDBC存储的表前缀。
4、并发启动与重复启动用建表隔离级别兜底
当两个进程几乎同时启动同一个作业时,冲突往往发生在创建JobInstance与JobExecution的那一瞬间。Spring Batch文档说明create相关方法的隔离级别用于保证同一作业并发启动时只有一个成功,默认SERIALIZABLE偏保守,但多数数据库READ_COMMITTED也能工作,按你的数据库与吞吐要求选一个可接受的值并固化在配置里。
5、连接池与权限分离用来阻断误写
批处理数据源账号只授予对元数据表的最小权限,业务数据源账号只授予业务表权限,哪怕有人在代码里误把JdbcTemplate指向了另一套数据源,也更容易在测试期暴露权限异常,而不是悄悄写错库。
6、启动期做一次自检把问题前置
在应用启动完成后打印两套DataSource的jdbcUrl与用户名信息到日志,或者做一次轻量的健康检查,分别执行一条只读校验语句与一条元数据表存在性校验,让配置错误在上线前就能被监控与告警捕捉。
三、Spring Batch元数据与业务库边界
当你把数据源拆开后,很多团队会顺带遇到另一个问题:到底哪些东西算元数据,哪些算业务状态,哪些又应该被当作幂等与重跑控制的依据。把边界讲清楚,才能在重跑、补数、回滚与审计场景里不互相打架。
1、元数据记录的是作业执行事实不是业务结果
元数据表记录JobInstance、JobExecution、StepExecution及其状态与时间,这些信息用于判断是否允许重跑、是否属于同一实例、是否可以从断点续跑,它不应该承载业务计算结果或业务校验中间态。
2、业务库负责承载最终可追溯的业务状态
作业输出的业务数据、处理标记、对账状态、错误明细等都应落在业务库,并由业务侧的数据模型定义一致性规则。批处理元数据只负责告诉你这次作业跑到了哪里,不负责定义业务正确性。
3、重跑与幂等需要同时利用两边信息
是否允许再次启动同一作业实例可以由元数据判断,是否允许再次写入同一批业务数据要由业务库幂等设计决定。常见做法是业务表引入批次号与唯一键约束,配合元数据里的作业参数形成稳定的重跑边界。
4、版本升级与建表变更要纳入统一的数据库变更流程
Spring Batch从5开始移除了基于Map的作业仓库实现并强调使用JDBC型JobRepository,这意味着元数据表与数据源在生产里更常态化存在,表结构变更与初始化脚本应被当作正式的数据库资产来管理,而不是临时启动时顺手建一下。
5、跨环境一致性靠的是同一套配置口径
开发环境可以用更轻的批处理库,生产环境用独立实例或独立schema,但表前缀、事务管理器归属、以及哪个DataSource供JobRepository使用这三件事在各环境必须保持一致,否则你会在环境切换时遇到同一套代码不同表现的问题。
总结
Spring Batch要配置多个数据源,核心是把批处理元数据通道与业务读写通道分开,业务DataSource用 Primary稳定默认注入,批处理DataSource用 BatchDataSource让批处理自动配置准确选中,同时配套 BatchTransactionManager把事务边界标清。防止冲突的关键在于显式引用、事务不串用、表前缀隔离、并发隔离级别控制,以及权限与连接池的物理隔离,把错误从运行期数据污染前置为启动期即可发现的配置问题。