ptj*_*ack 5 java sql-server spring spring-batch spring-boot
我正在使用 spring Batch 4.0.0、Spring Boot 2.2.0、java jdk 12.0.2、db sql server 2016。
多个 Spring Batch 作业同时执行会导致 Spring Batch 元数据表中出现死锁。
我尝试了不同的解决方案:
ALTER TABLE [OWN].[BATCH_JOB_EXECUTION_SEQ]
ADD CONSTRAINT [PK_BATCH_JOB_EXECUTION_SEQ] PRIMARY KEY CLUSTERED ([ID] ASC);
ALTER TABLE [OWN].[BATCH_JOB_SEQ]
ADD CONSTRAINT [PK_BATCH_JOB_SEQ] PRIMARY KEY CLUSTERED ([ID] ASC);
ALTER TABLE [OWN].[BATCH_STEP_EXECUTION_SEQ]
ADD CONSTRAINT [PK_BATCH_STEP_EXECUTION_SEQ] PRIMARY KEY CLUSTERED ([ID] ASC);
Run Code Online (Sandbox Code Playgroud)
@Override
protected JobRepository createJobRepository() throws Exception {
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setDatabaseType(DATABASE_TYPE);
factory.setIsolationLevelForCreate(ISOLATION_REPEATABLE_READ);
factory.setDataSource(dataSource);
factory.setTransactionManager(getTransactionManager());
factory.setTablePrefix(TABLE_PREFIX);
factory.afterPropertiesSet();
return factory.getObject();
}
@Override
protected JobExplorer createJobExplorer() throws Exception {
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
jobExplorerFactoryBean.setDataSource(dataSource);
jobExplorerFactoryBean.afterPropertiesSet();
jobExplorerFactoryBean.setTablePrefix(TABLE_PREFIX);
return jobExplorerFactoryBean.getObject();
}
Run Code Online (Sandbox Code Playgroud)
但是当我开始多项工作时,我经常/总是遇到相同的错误:
无法增加身份;嵌套异常是 com.microsoft.sqlserver.jdbc.SQLServerException:事务在锁资源上与另一个进程发生死锁,并已被选为死锁受害者。重新运行事务。
我已经找到解决方案并发布给有同样问题的人;我从未找到完整且全面的解决方案,因此我想解释如何解决它以及为什么会发生,让我们开始吧。这个死锁是由 BATCH_STEP_EXECUTION_SEQ、BATCH_JOB_EXECUTION_SEQ 和 BATCH_JOB_SEQ 表的更新和删除引起的,但为什么会发生呢?也许是因为事情没有得到适当的管理,但真正的问题是sql server开始使用sql server 2012中的序列,所以spring批处理必须使用这个表来管理序列,对于那些使用旧版本的人来说,我没有解决方案sql server 版本高于 2012 年,本指南仅适用于 2012 年以后的版本。
1. 在数据库中创建序列。
请注意从尚未处理的 ID 开始序列,否则如果从 1 开始,则必须清理 Spring 表以及可能使用作业 ID 的表。
Run Code Online (Sandbox Code Playgroud)DROP TABLE [OWN].[BATCH_JOB_SEQ] GO DROP TABLE [OWN].[BATCH_JOB_EXECUTION_SEQ] GO DROP TABLE [OWN].[BATCH_STEP_EXECUTION_SEQ] GO CREATE SEQUENCE [OWN].[BATCH_JOB_SEQ] AS [bigint] START WITH 1000 INCREMENT BY 1 MINVALUE -9223372036854775808 MAXVALUE 9223372036854775807 CACHE GO CREATE SEQUENCE [OWN].[BATCH_JOB_EXECUTION_SEQ] AS [bigint] START WITH 1000 INCREMENT BY 1 MINVALUE -9223372036854775808 MAXVALUE 9223372036854775807 CACHE GO CREATE SEQUENCE [OWN].[BATCH_STEP_EXECUTION_SEQ] AS [bigint] START WITH 1000 INCREMENT BY 1 MINVALUE -9223372036854775808 MAXVALUE 9223372036854775807 CACHE GO
2. 也重写 JobRepository 方法和 JobExplorer (我个人必须添加它,否则我会遇到有关 spring 批处理表前缀的问题)。
将隔离级别设置为 REPEATABLE_READ,否则您将在数据库上出现新的并发错误(就个人而言,如果没有此设置,我在 BATCH_JOB_EXECUTION 或 BATCH_JOB_INSTANCE 上遇到问题,我现在不记得了)。
Run Code Online (Sandbox Code Playgroud)@Component public class SQLServerConfig extends DefaultBatchConfigurer{ @Autowired private DataSource dataSource; @Value("${spring.batch.tablePrefix}") private String tablePrefix; @Override protected JobRepository createJobRepository() throws Exception { JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean(); factory.setDatabaseType(DatabaseType.SQLSERVER.name()); factory.setDataSource(dataSource); factory.setTransactionManager(getTransactionManager()); factory.setTablePrefix(tablePrefix); factory.setIncrementerFactory(new CustomDataFieldMaxValueIncrementerFactory(dataSource)); factory.setIsolationLevelForCreate("ISOLATION_REPEATABLE_READ"); factory.afterPropertiesSet(); return factory.getObject(); } @Override protected JobExplorer createJobExplorer() throws Exception { JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean(); jobExplorerFactoryBean.setDataSource(dataSource); jobExplorerFactoryBean.afterPropertiesSet(); jobExplorerFactoryBean.setTablePrefix(tablePrefix); return jobExplorerFactoryBean.getObject(); } }
3. 设置 CustomDataFieldIncrementerFactory,因为现在它开始管理它的序列。
Run Code Online (Sandbox Code Playgroud)public class CustomDataFieldMaxValueIncrementerFactory extends DefaultDataFieldMaxValueIncrementerFactory { private DataSource dataSource; public CustomDataFieldMaxValueIncrementerFactory(DataSource dataSource) { super(dataSource); this.dataSource = dataSource; } @Override public DataFieldMaxValueIncrementer getIncrementer(String incrementerType, String incrementerName) { DatabaseType databaseType = DatabaseType.valueOf(incrementerType.toUpperCase()); DataFieldMaxValueIncrementer dataFieldMaxValueIncrementer = null; if (databaseType == DatabaseType.SQLSERVER) { dataFieldMaxValueIncrementer = new CustomSqlServerMaxValueIncrementer(dataSource, incrementerName); } else { dataFieldMaxValueIncrementer = super.getIncrementer(incrementerType, incrementerName); } return dataFieldMaxValueIncrementer; } }
就这样,现在一切正常了!!!
我希望它对某人有用。
| 归档时间: |
|
| 查看次数: |
2211 次 |
| 最近记录: |