无法复制java.sql.sql异常:ORA-00060:等待资源时检测到死锁



环境:WildFly10.1,EJB3.1、JPA、Oracle

这个问题看起来和以前问的问题相似,但我没能得到答案。

我有一个处理支付的多线程批处理应用程序:EJB方法(由Timer Bean触发)从DB中检索未处理的支付,将支付拆分为块,然后将这些块发送到另一个EJB方法(Async)进行处理。下面提供的示例代码:

@Stateless
@LocalBean
public class PaymentProcessor {
@EJB
private PaymentFacade paymentFacade;
@EJB
private PaymentExecuter paymentExecuter;
public void processNegativeTrackerBatch() {
List<Payment> paymentList = paymentFacade.findPendingPaymentEntries();
// Each PaymentBlock represent a thread
List<PaymentBlock> paymentBlockList = splitPaymentsIntoBlocks(paymentList, NUMBER_OF_THREADS);
for (PaymentBlock block : paymentBlockList) {
paymentExecuter.processPayment(block.paymentList());
}
}
}

@Stateless
@LocalBean
public class PaymentExecuter {
@EJB
private PaymentFacade paymentFacade;
@Asynchronous
public Future<PaymentResults> processPayment(List<Payment> paymentList) {
PaymentResults paymentResults = new PaymentResults("SUCCESS");
for (Payment payment : paymentList) {
try {
//update acount balances (code removed)
//The following code cause oracle to threw ORA-00060: deadlock detected while waiting for resource
payment.setLoaded((short) 1);
payment.setDateLoaded(new Date());
paymentFacade.edit(payment);
} catch (Exception ex) {
paymentResults.setResponseCode("PARTIAL FAIL");
//log exception
}
}
return new AsyncResult<PaymentResults>(paymentResults);
}
}

JTA在wildfly中设置为true时,上面的代码在生产环境中引发以下异常:

javax.ejb.EJBTransactionRolledbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.3.v20160218-180e602): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLException: ORA-00060: deadlock detected while waiting for resource
Error Code: 60
Call: UPDATE PAYMENT SET DATE_LOADED = ?, LOADED = ? WHERE (ID = ?)
bind => [2018-07-05 01:07:22.225, 1, 650133]
Query: UpdateObjectQuery(za.co.company.persistence.entities.Payment[ id=650133 ])
at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleInCallerTx(CMTTxInterceptor.java:159)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInCallerTx(CMTTxInterceptor.java:256)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.required(CMTTxInterceptor.java:329)
at org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:239)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:47)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:100)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ejb3.deployment.processors.StartupAwaitInterceptor.processInvocation(StartupAwaitInterceptor.java:22)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:67)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:54)
at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:340)
at org.jboss.invocation.ContextClassLoaderInterce

然而,即使使用完全相同的数据,我也无法在预生产环境中成功复制上述事件。

我的问题:

  1. 如果付款是唯一的,并且我确信没有任何付款记录最终会出现在多个线程中,为什么会出现死锁?

  2. 如何复制死锁

  3. 我如何才能发现真正发生的事情,以便修复它?

请协助。感谢对材料的任何帮助或推荐。

以下是有关oracle跟踪的一些信息:

2018-07-05 01:02:39.569*:ksq.c@12954:ksqdld_hdr_dump():检测到死锁(ORA-00060)请参阅My Oracle Support for Troubleshooting ORA-60 Errors 上的注释60.1

【交易僵局】

以下死锁不是ORACLE错误。这是一个应用程序设计中用户错误导致的死锁或者由于发布不正确的ad-hoc SQL。以下内容信息可能有助于确定死锁:

死锁图:------------阻塞器----------------服务员------------资源名称进程会话保持等待串行进程会话保持等候串行TX-00290020-0016FF24-0000000000000000 161 463 X 908 173 467 S 29672TX-0027000C-001C7991-00000000-00000000 173 467 X 29672 161 463 S 908

***2018-07-05T01:02:39.7575960+02:00dbkedDefDump():启动非事件诊断转储(标志=0x0,级别=1,掩码=0x0)-----错误堆栈转储----------此会话的当前SQL语句(SQL_id=6aju12kbrg657)-----更新已加载的付款集=:1,DATE_LOADED=:2其中(ID=:3)

Oracle SQL绑定捕获详细信息

"锁冲突"是指两个进程试图同时更新同一行。

然而,"僵局"是另一种动物。当两个进程试图更新相同的资源,但顺序不同时,就会发生死锁。一种常见的情况是,进程A尝试更新表Y,然后更新表Z。同时,进程B尝试更新表Z,然后更新表格Y。进程A锁定了表格Y,需要锁定Z,但进程B锁定了表格Z,需要锁定Y。两者都不能继续,因为另一个进程锁定了他们需要的资源,因此,他们将等到世界末日,或者直到数据库决定让他们的一个事务失败,从而释放他们持有的锁,并允许其他被阻止的进程继续进行。

因此,很明显,在您的代码库中存在这样的情况——表以不同的顺序更新。不确定为什么JTA设置可能会影响事情,但这就是正在发生的事情。

祝你好运。

此错误与您在此处发布的Java代码无关。

在您的Payment和PaymentFacade表中验证他们正在更新哪些数据库表或其他资源。可能有多个表/资源参与同一更新请求(方法调用)。

当两个数据库会话(来自线程、进程或另一台计算机的连接)以不同的顺序修改两个或多个资源时,通常会发生这种冲突。例如:

  • 会话1更新第1行-更新并锁定第1行以保持一致性,直到事务完成
  • 会话2更新第2行-更新并锁定第2行以保持一致性,直到事务完成
  • 会话1更新第2行-等待第2行解锁后再更新
  • 会话2更新第1行-等待第1行解锁后再更新

两个会话现在都在等待资源/行时相互阻塞。这永远不会结束。但数据库检测到这种情况,并在五秒钟后(取决于数据库设置)抛出死锁异常。只有一个会话会出现此异常。另一个继续事务并对未锁定的记录执行更新。

您可以通过回滚然后重做整个事务来解决此问题。例如,重复sql语句或通过重新启动线程。但首先要检查一下,如果冲突发生在你的线程之间。在这种情况下,你可以解决它。如果更新影响多个表,请在线程之间拆分yob,同时记住这些表。始终按照相同的顺序执行更新。例如,按表的主键顺序对要执行的更新进行排序。如果存在与其他应用程序冲突的原因,则应该实现事务重做或线程重新启动。

在寻找解决方案方面一切最佳

最新更新