运行弹簧批处理作业的多个实例时出现死锁

Posted

技术标签:

【中文标题】运行弹簧批处理作业的多个实例时出现死锁【英文标题】:Deadlock while running multiple instances of a spring batch job 【发布时间】:2021-06-09 04:17:18 【问题描述】:

我有一个 spring 批处理作业,它在基于块的步骤中从数据库读取并在进行一些处理后写入文件。 我的要求是同时运行几乎 16 个作业实例,只是使用不同的作业参数。

但我在这样做的过程中遇到了几个问题。

1.

无法为事务打开 JDBC 连接。嵌套异常是 java.sql.SQLTransientConnectionException: Hikaripool -1 - Connection is not available。

异常:无法增加标识。嵌套异常是 com.microsoft.SQLserver.jdbc.SQLServerException: Transaction (process ID 124) was deadlocked on lock resources with other process,并已被选为死锁牺牲品。重新运行事务。

我已经尝试了链接Github link 中提供的解决方案,方法是设置IsolationLevel 并更改元数据表,如下所示。

像这样设置 IsolationLevelForCreate

JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
factory.setIsolationLevelForCreate("ISOLATION_REPEATABLE_READ");

让 DBA 像这样为每个 SEQ 表添加索引(JET 是我将 repo 表放入的架构):

ALTER TABLE [JET].[BATCH_JOB_EXECUTION_SEQ]
ADD CONSTRAINT [BATCH_JOB_EXECUTION_SEQ_PK] PRIMARY KEY CLUSTERED ([ID] ASC)
GO

ALTER TABLE [JET].[BATCH_JOB_SEQ]
ADD CONSTRAINT [BATCH_JOB_SEQ_PK] PRIMARY KEY CLUSTERED ([ID] ASC)
GO

ALTER TABLE [JET].[BATCH_STEP_EXECUTION_SEQ]
ADD CONSTRAINT [BATCH_STEP_EXECUTION_SEQ_PK] PRIMARY KEY CLUSTERED ([ID] ASC)
GO

但我仍然面临这个问题。

PS:spring batch已经部署到AKS(Azure Kubernetes Services),使用Azure SQLServer作为数据源。

【问题讨论】:

要调查死锁,您检查死锁图,它会告诉您哪些资源导致了死锁,这通常足以解决如何修复它。否则,您只是在猜测并希望中奖。此外,您很可能无法消除所有死锁,在这种情况下需要重试系统。 【参考方案1】:

根据https://github.com/spring-projects/spring-batch/issues/1448 中的讨论,该问题似乎是由Spring Framework 中的SqlServerMaxValueIncrementer 未使用SQLServer 的本机序列引起的。以下是 Javadoc 的摘录:

There should be one sequence table per table that needs an auto-generated key.

Example:

create table tab (id int not null primary key, text varchar(100))
create table tab_sequence (id bigint identity)
insert into tab_sequence default values

这可能是由于 SQLServer 直到最近才支持序列。但我想这就是 Spring Batch 将tables to emulate sequences 用于 MS SQL Server 的原因。

我建议您尝试更改the default DDL 以使用序列而不是表格:

CREATE SEQUENCE BATCH_STEP_EXECUTION_SEQ ;
CREATE SEQUENCE BATCH_JOB_EXECUTION_SEQ ;
CREATE SEQUENCE BATCH_JOB_SEQ ;

这是基于 MS SQL Server 的docs 的默认序列定义。这应该可以,但您可以根据需要自定义它们。

您可能还需要提供基于序列的自定义 DataFieldMaxValueIncrementer(因为 Spring Framework 中的序列使用表)并通过 DataFieldMaxValueIncrementerFactory 将其注册到 Spring Batch 中(请参阅 JobRepositoryFactoryBean#setIncrementerFactory)。

【讨论】:

以上是关于运行弹簧批处理作业的多个实例时出现死锁的主要内容,如果未能解决你的问题,请参考以下文章

一个实例有多个 GPU 或多个实例有一个 GPU

在Spring中运行SAME cron作业的多个实例

跨多个服务器的单个作业执行

使用 Apache Commons Exec 向命令提供多个输入并提取输出时出现问题

当 Hangfire 并行处理多个作业时,为啥 MySQL InnoDB 会产生如此多的死锁?

SQLException : 处理 2 个不同的表时出现死锁错误