多个消费者在 RestImpl 的某处导致 LockAcquisitionExecption class

Multiple Consumers are Causing LockAcquisitionExecption somewhere in RestImpl class

我的应用程序正在从 IBM Mq 批量接收 JMSTextMessages。对于每条消息,我都调用 REST API 来处理这些消息。当 API 正在处理这些消息时,我间歇性但非常频繁地收到以下异常日志:

java.lang.Exception: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not execute statement

2018-04-03 17:54:10.614 ERROR 5259 --- [.0-8083-exec-59] o.h.engine.jdbc.spi.SqlExceptionHelper   : ORA-00060: deadlock detected while waiting for resource

它既不打印 table 也不打印它试图执行的查询。为了解决这个问题,我尝试在我的 REST API 服务代码中将所有数据库处理代码推送到一个同步块中,但对于这个问题仍然相同 issue.Due 每第二条消息都无法处理。

基于大量 material 在线可用,似乎收到的每条消息都会触发一个新线程(在 REST API 服务方法结束时),从而导致一些死锁行为我的应用程序。可悲的是,我无法弄清楚到底是哪段代码导致了这种情况,而且在服务应用程序启动方面我们只有一个线程。

所以现在,我决定在侦听器端开始处理另一条消息之前找出是否可以引入延迟。再次阅读很多书,到处都可以看到 XML 属性 更改来处理这个问题,但是如果我想通过 Spring 注释来做的话,没有什么方便的。我的应用程序没有任何 XML 配置。

这是我的伪代码:

@JmsListener(destination="queue_name")
public void receiveMessage(Object message){
     myRestService.processMessage(message);
}

ServiceImpl 代码

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processMessage(Object message){

Long id = getIdFromMessage(message); 

X x = readFromDB(id);

updateDataInX(x, message);

savex(x);
}

我还要强调一下,我在数据库中有父子关系。其中父 table 的 PRIMARY_KEY 是子 table 的 PRIMARY_KEY (以及 FK),它也被索引。当我在上述方法 (updateDataInX) 中更新值时,一些列在子 table 行中更新,一些列在父 table 行中更新(映射到子行)。实例 x 属于 CHILD class.

除了引入延迟之外,我该如何解决这个问题?生产者是一个单独的应用程序,他们在一秒钟内发送多条消息——消费者显然无法处理这些消息。这完全是后端更新,所以我不介意引入延迟来拥有故障安全机制,但我没有找到任何带有 spring 注释的合适解决方案。另外,如果您能说出接收器端和其余 API 端的线程到底发生了什么。自从过去 3 周以来,我对它的阅读感到非常困惑。

我的代码还涉及在另一个 CHILD table 中添加 AUDIT 日志记录数据,它指的是同一 PARENT table 的 NON-PRIMARY 键(X 的层次结构).

在 CHILD table 中插入新行时,oracle 正在使用完整的 table 锁,这导致所有后续插入都因死锁问题而失败。

对 CHILD table 进行完全 table 锁定的原因是它有一个引用 PARENT table 的外键列 REFERENCE_NO。

当来自多个会话的SQL语句涉及parent/child tables时,如果子table中的外键列使用oracle默认锁定机制可能会发生死锁没有索引。当父 table 中的主键值更改时,Oracle 也会对子 table 进行完全 table 锁定。

在我们的例子中,PARENT.REFERENCE_NO 是一个 non-primary 键,在 CHILD table 中被称为外键。每次我们在 PARENT table 上进行状态更新时,如上面的伪代码所述,hibernate 会在 REFERENCE_NO 上触发更新,这会导致锁定整个 CHILD table,使其无法用于 AUDIT 日志记录插入.

所以他们的关键是:Oracle 建议对这样的外键列进行索引,因为 oracle 不会完全 table 锁定。