【问题标题】:Unable to replicate java.sql.SQLException: ORA-00060: deadlock detected while waiting for resource无法复制 java.sql.SQLException:ORA-00060:等待资源时检测到死锁
【发布时间】:2018-07-15 21:59:21
【问题描述】:

环境: WildFly10.1, EJB3.1、JPA、甲骨文

这个问题看起来与之前提出的问题相似,但我无法得到答案。

我有一个处理付款的多线程批处理应用程序:一个 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 161 463 X 908 173 467 S 29672 TX-0027000C-001C7991-00000000-00000000 173 467 X 29672 161 463 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

【问题讨论】:

  • 当出现死锁错误时,会生成一个死锁跟踪文件,该文件将为您提供有关哪些进程发生冲突、它们持有哪些锁以及它们试图获取哪些锁的信息。该跟踪文件应该为您提供很多有用的信息。
  • 感谢您的回复@JustinCave。我确实有一个跟踪文件。我找不到比我在 java 错误日志中看到的更好的信息,但是当我开始核心数据库管理工作时,我并没有那么清楚。我怎样才能学会更好地阅读这个文件?
  • 网上有几篇关于阅读死锁痕迹的文章(这里有一个SO问题,例如stackoverflow.com/questions/17358088/…)。但是,如果您将死锁跟踪的相关部分编辑到您的问题中,您可能会获得更多帮助。
  • 谢谢贾斯汀。我会看看这个。

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


【解决方案1】:

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

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

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

祝你好运。

【讨论】:

  • 感谢@Bob 的回复。我可以假设当您说表格以不同的顺序更新时,您的意思是表格中的行以不同的顺序更新吗?否则,您是否暗示每次进程更新一行时都会锁定整个表?在我的情况下,线程/进程包含唯一的行,并且永远不应该出现不同线程同时更新同一行的情况,除非第二个进程在我的应用程序之外......
【解决方案2】:

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

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

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

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

两个会话现在在等待资源/行时互相阻塞。 这永远不会结束。但是数据库检测到这种情况并在几秒钟后(取决于数据库设置)引发死锁异常。 只有一个会话获得此异常。另一个继续事务并在解锁记录上执行更新。

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

尽全力寻找解决方案

【讨论】:

    猜你喜欢
    • 2011-03-05
    • 2018-04-27
    • 2016-01-22
    • 2013-09-04
    • 2021-04-20
    • 2016-09-02
    • 1970-01-01
    • 2015-11-14
    • 1970-01-01
    相关资源
    最近更新 更多