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

时间:2018-07-15 21:59:22

标签: oracle jpa wildfly java-ee-6 ejb-3.1

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

该问题看起来与之前提出的问题相似,但是我无法获得答案。

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

@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);

    }
}

wildfly中将JTA设置为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上的注释60.1,以对ORA-60错误进行故障排除

[交易死锁]

以下死锁不是ORACLE错误。它是一个 由于应用程序设计中的用户错误而导致死锁 或发出不正确的临时SQL。下列 信息可能有助于确定僵局:

死锁图:                                           ------------堵漏者----------- ----------------侍者------- ----- 资源名称进程会话保留等待串行进程会话保留等待串行 TX-00290020-0016FF24-00000000-00000000 161463 X 908173467 S 29672 TX-0027000C-001C7991-00000000-00000000 173467 X 29672161463 S 908

*** 2018-07-05T01:02:39.575960 + 02:00 dbkedDefDump():启动非事件诊断转储(标志= 0x0,级别= 1,掩码= 0x0) -----错误堆栈转储----- -----此会话的当前SQL语句(sql_id = 6aju12kbrg657)----- UPDATE PAYMENT SET LOADED =:1,DATE_LOADED =:2 WHERE(ID =:3)

Oracle SQL Bind capture details

1 个答案:

答案 0 :(得分:0)

“锁冲突”是两个进程试图同时更新同一行的地方。

但是,“僵局”是另一种动物。当两个进程尝试更新相同资源但顺序不同时,就会发生死锁。常见的情况是,进程A尝试先更新表Y,然后再更新表Z。同时,进程B尝试先更新表Z,然后再更新表Y。进程A的表Y被锁定,并且需要锁定Z-进程B的表Z被锁定。已锁定,需要锁定Y。由于另一个进程拥有他们需要锁定的资源,因此它们都无法继续进行,因此它们将等到世界末日-或直到数据库决定使其中一个事务失败,从而释放它们持有的锁并允许其他受阻进程继续进行。

因此,显然在您的代码库中可能发生这种情况-表以不同的顺序更新。不知道为什么JTA设置可能会影响事物,但这就是正在发生的事情。

好运。