【问题标题】:Deadlocked transactions with Hibernate on Oracle在 Oracle 上使用 Hibernate 的死锁事务
【发布时间】:2015-06-13 14:29:45
【问题描述】:

我有以下(简化的)Hibernate 实体:

@Entity(name = "Foo")
public class Foo {

    @Id
    @GeneratedValue
    public int id;

    @OneToOne
    public Bar bar;
}

和,

@Entity(name = "Bar")
public class Bar {

    @Id
    @GeneratedValue
    public int id;

    @Column
    public String field;

    @Version
    public int version;

}

我在一个大致如下所示的事务中更新这些实体:

Bar bar = findBar(em);
Foo foo = findFoo(em);

bar.field = "updated value";

if (<condition>) {
    em.remove(foo);
}

em.detach(bar);
em.merge(bar);

请注意,em.remove(foo) 只是有时会被调用,而 bar 总是会更新。

我注意到运行应用程序时偶尔出现ORA-00060: Deadlock detected 错误。转储似乎表明两个死锁会话被锁定在 em.merge(bar)em.remove(foo),但我不明白为什么会这样。

这段代码怎么会死锁?有没有办法对其进行重组以避免死锁?

以下是跟踪中的一些额外信息:

Deadlock graph:
                       ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TX-00040005-000010dd        73    6557     X             81    6498           X
TX-00010018-000010bd        81    6498     X             73    6557           X

session 6557: DID 0001-0049-000002F5    session 6498: DID 0001-0051-0000030E 
session 6498: DID 0001-0051-0000030E    session 6557: DID 0001-0049-000002F5 

Rows waited on:
  Session 6557: obj - rowid = 00004797 - AAAEeXAB4AAADH0BBP
  (dictionary objn - 18331, file - 120, block - 12788, slot - 15)
  Session 6498: obj - rowid = 00007191 - AAAHGRAB4AAAACBBBo
  (dictionary objn - 29041, file - 120, block - 129, slot - 40)

----- Information for the OTHER waiting sessions -----
Session 6498:
program: JDBC Thin Client
    application name: JDBC Thin Client, hash value=2546894660
  current SQL:

delete from Foo where id=:1 

----- Current SQL Statement for this session (sql_id=sfasdgasdgaf) -----
update Bar set field=:1, version=:2 where id=:3 and version=:4 

【问题讨论】:

  • 也许真的不可能重现您的问题。如果它对你有帮助,请看这个lalitkumarb.wordpress.com/2014/02/25/…
  • 您是否可以跟踪完整的会话?
  • @LalitKumarB 我有跟踪,从那里我将其追溯到那些合并和删除的调用。我看到那是两个锁定的会话。您想从跟踪中获得任何其他信息吗?

标签: java oracle hibernate jpa transactions


【解决方案1】:

如果我对detach 的理解正确,您应该这样做:

Foo foo = findFoo(em);
Bar bar = findBar(em);

if (<condition>) {
    em.remove(foo);
    em.detach(bar); //If it is really necessary
    em.flush();
}

bar = findBar(em); //It will reattach the entity on the persistence context
bar.field = "updated value";

em.merge(bar); 
em.commit();

【讨论】:

  • 所以这最终解决了我的问题。不确定是由于切换操作顺序,还是显式的 flush()、detach() 调用。您能否详细说明在事务中实际获取和释放锁的时间?
  • 当你分离一个实体时,它不能再被持久化(锁定)。进行查找将加载同一记录的新实体。这个不是分离的,所以你可以毫无问题地合并。
【解决方案2】:

Oracle中发生死锁一般有两个主要原因

  • 所谓的 SX 到 SSX 锁升级。这是由于 FK(子表)上缺少索引引起的。在这种情况下,Oracle 必须在验证约束之前锁定整个子表。 See AskTom Artice
  • SQL 语句顺序错误

在所有情况下,死锁都是由应用程序错误引起的。您将需要来自数据库服务器的死锁报告(.trc 文件)。在那里你会发现所涉及的 SQL 语句和表。由于您使用 Hibernate,您很难预测 SQL 语句的执行顺序,有时它可能有助于扩展实体管理器缓存,以防止过早调用 flush()

编辑: 好的,所以你有 TX(X) 锁。这些是行级别,而 SSX 是表级别。那么死锁对象可能是表中的一行,也可能是索引中的唯一键。跟踪文件还应包含每个会话的先前语句以及游标(SQL 语句执行的位置),游标还应包含绑定变量的值。

尝试执行:

select * from Foo where rowid = 'AAAHGRAB4AAAACBBBo';
select * from Bar where rowid = 'AAAEeXAB4AAADH0BBP';
  • 您真的使用 CamelCase 作为表名吗?
  • 什么是“Foo”和“Bar”的 DDL?
  • 在 Foo 和 Bar 之间放置 FK 时是否会发生死锁?
  • 当您只调用em.remove(foo); 时,子栏也会被删除吗?

【讨论】:

  • 我从 trc 中提取了这些信息。我用它来追溯与问题中提到的这两个实体的交易。您是否建议向 foo 实体中的 bar 引用添加索引可以解决此问题?
  • 如果您的死锁报告包含锁类型SXSSX,那么这是第一种情况。然后你需要在FOO.BAR 上创建一个索引,因为 FOO 是 BAR 表的子表。而FOO.BAR 指向BAR.ID
  • 死锁跟踪报告表明SX的两种锁类型。 SSX 也不是
  • 那么您应该使用跟踪标题更新问题。发布死锁图,涉及的 SQL 语句,以及(可能)它们的执行计划。如果您无法将此信息发布到 SO 等公共网站上,则必须联系 Oracle 支持。
  • 好的,所以你有 TX(X) 锁。这些是行级别,而 SSX 是表级别。那么死锁对象可能是表中的一行,也可能是索引中的唯一键。
猜你喜欢
  • 2016-04-01
  • 1970-01-01
  • 2020-03-19
  • 2014-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多