环境: 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
...但是,即使在数据完全相同的情况下,我也未能在预生产环境中成功复制上述事件。
我的问题:
如果付款是唯一的,并且我确定没有付款记录会出现在多个线程中,那么为什么会出现死锁?
如何复制死锁
我该如何发现真正发生的事情以便修复它?
请协助。任何帮助或推荐给资料的人。
以下是有关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)
答案 0 :(得分:0)
“锁冲突”是两个进程试图同时更新同一行的地方。
但是,“僵局”是另一种动物。当两个进程尝试更新相同资源但顺序不同时,就会发生死锁。常见的情况是,进程A尝试先更新表Y,然后再更新表Z。同时,进程B尝试先更新表Z,然后再更新表Y。进程A的表Y被锁定,并且需要锁定Z-进程B的表Z被锁定。已锁定,需要锁定Y。由于另一个进程拥有他们需要锁定的资源,因此它们都无法继续进行,因此它们将等到世界末日-或直到数据库决定使其中一个事务失败,从而释放它们持有的锁并允许其他受阻进程继续进行。
因此,显然在您的代码库中可能发生这种情况-表以不同的顺序更新。不知道为什么JTA设置可能会影响事物,但这就是正在发生的事情。
好运。