Oracle 需要帮助了解SELECT的行为。。。用于导致死锁的更新

Oracle 需要帮助了解SELECT的行为。。。用于导致死锁的更新,oracle,jpa,deadlock,Oracle,Jpa,Deadlock,我有两个并发事务执行这段代码(为便于说明而简化): @Transactional 公共帐户(){ List users=em.createQuery(“从用户中选择u”,User.class) .setLockMode(LockModeType.悲观写入) .getResultList(); for(用户:用户){ em.remove(用户); } } 我的理解是,其中一个事务(比如事务A)应该首先执行SELECT,锁定它需要的所有行,然后继续删除,而另一个事务应该在执行SELECT之前等待A

我有两个并发事务执行这段代码(为便于说明而简化):

@Transactional
公共帐户(){
List users=em.createQuery(“从用户中选择u”,User.class)
.setLockMode(LockModeType.悲观写入)
.getResultList();
for(用户:用户){
em.remove(用户);
}
}

我的理解是,其中一个事务(比如事务A)应该首先执行SELECT,锁定它需要的所有行,然后继续删除,而另一个事务应该在执行SELECT之前等待A的提交。但是,此代码是死锁的。我错在哪里?

当您使用悲观的JPA时,通常会将其选中以进行更新,这会在数据库中创建一个锁,这对某一行不是必需的,这取决于数据库以及如何配置锁,默认情况下,锁是按页或按块而不是按行,因此,请检查您的数据库文档以确认您的数据库是如何进行锁定的,您还可以对其进行更改,以便为一行应用锁定。
当你调用deleteAccounts方法时,它会启动一个新的事务,并且锁将一直处于活动状态,直到事务提交(或回滚)为止。在这种情况下,当该方法完成时,如果其他事务想要获得相同的锁,它不能,我想这就是为什么你有死锁,我建议您尝试另一种机制,可能是乐观锁,也可能是实体锁

您可以在给定超时的情况下尝试获取锁,以便:

em.createQuery("select u from User", User.class)
.setLockMode(LockModeType.PESSIMISTIC_WRITE)
.setHint("javax.persistence.lock.timeout", 5000)
.getResultList();
我发现了一个很好的解释这个错误的方法,它是由数据库引起的:

Oracle会自动检测死锁并通过滚动解决 返回死锁中涉及的一个事务/语句,因此 释放该事务锁定的一组资源/数据。这个 回滚的会话将观察到Oracle错误:ORA-00060: 等待资源时检测到死锁。甲骨文还将生产 数据库UDUMP目录下跟踪文件中的详细信息

这些死锁通常是由 在同一事务和多个数据库中涉及多个表更新 应用程序/事务同时在同一个表上运行 时间通过在中锁定表,可以避免这些多表死锁 所有应用程序/事务中的顺序相同,因此防止 死锁条件


用户表可能有很多外键引用它。如果其中任何一个未编制索引,Oracle将锁定整个子表,同时从父表中删除该行。如果多个语句同时运行,即使对于不同的用户,相同的子表也将被锁定。由于这些递归操作的顺序无法控制,因此多个会话可能会以不同的顺序锁定相同的资源,从而导致死锁

有关更多信息,请参阅《概念手册》中的

要解决此问题,请向任何未索引的外键添加索引。如果列名是标准的,那么这样的脚本可以帮助您找到潜在的候选者:

--Find un-indexed foreign keys.
--
--Foreign keys.
select owner, table_name
from dba_constraints
where r_constraint_name = 'USER_ID_PK'
    and r_owner = 'THE_SCHEMA_NAME'
minus
--Tables with an index on the relevant column.
select table_owner, table_name
from dba_ind_columns
where column_name = 'USER_ID';

死锁的异常消息是什么?异常[EclipseLink-4002](Eclipse持久性服务-2.5.1.v20130918-f2b9fc5):org.eclipse.persistence.exceptions.DatabaseException内部异常:java.sql.SQLException:ORA-00060:等待资源时检测到死锁错误代码:60您在那里的用户之间有关系吗?没有。有@OneToOne、cascading、与其他实体的关系,但是。您确定没有其他实体类型锁定在另一个并发事务中吗?您能确认这种情况经常发生吗?“如果其他事务想要获得相同的锁,它不能”-但是会有一个超时,而不是死锁,对吗?因为这个较新的事务还没有获取任何信息,所以应该没有死锁的原因。是的,这是真的,但您可以指定超时0,这是一种奇怪的行为,但是如果您看到异常是抛出死锁的数据库,它说在等待资源时检测到死锁,也许您可以为测试指定5秒的超时。供应商提供程序可以配置等待锁定的时间,您可以将超时值指定为提示,我编辑我的答案是为了展示一个例子。它失败了,在超时过期之前出现了死锁。在谷歌搜索了错误代码后,我找到了一篇文章,更好地解释了错误在Oracle中出现的原因,我用链接挑剔编辑了我的答案:数据库中可能有几个用户拥有一个名为“USER\u ID\u PK”的约束,所以这个查询可能会返回误报。我建议在第一个子查询中添加
和r\u owner=…
--Find un-indexed foreign keys.
--
--Foreign keys.
select owner, table_name
from dba_constraints
where r_constraint_name = 'USER_ID_PK'
    and r_owner = 'THE_SCHEMA_NAME'
minus
--Tables with an index on the relevant column.
select table_owner, table_name
from dba_ind_columns
where column_name = 'USER_ID';