在 Oracle 上使用 Hibernate 的死锁事务
Deadlocked transactions with Hibernate on Oracle
我有以下(简化的)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
一般Oracle中出现死锁的原因主要有两个
- 所谓的 SX 到 SSX 锁定升级。这是由于 FK(child table) 上缺少索引引起的。在这种情况下,Oracle 必须在验证约束之前锁定整个子 table。 See AskTom Artice
- SQL 语句顺序错误
在所有情况下,死锁都是由应用程序错误引起的。您将需要来自数据库服务器的死锁报告(.trc 文件)。您会发现涉及 SQL 语句和 tables。由于您使用 Hibernate,您很难预测 SQL 语句执行的顺序,有时它可能有助于扩展实体管理器缓存,以防止过早调用 flush()
.
已编辑:
好的,所以你有 TX(X) 锁。这些是行级别,而 SSX 是 table 级别。那么死锁对象可能是 table 中的一行或索引中的唯一键。跟踪文件还应包含每个会话的先前语句以及游标(SQL 语句执行的位置),游标还应包含绑定变量的值。
尝试执行:
select * from Foo where rowid = 'AAAHGRAB4AAAACBBBo';
select * from Bar where rowid = 'AAAEeXAB4AAADH0BBP';
- table 名字你真的使用 CamelCase 吗?
- "Foo" 和 "Bar" 的 DDL 是什么?
- 在 Foo 和 Bar 之间放下 FK 是否会发生死锁?
- 只调用
em.remove(foo);
时child Bar也会被移除吗?
如果我理解正确 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();
我有以下(简化的)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
一般Oracle中出现死锁的原因主要有两个
- 所谓的 SX 到 SSX 锁定升级。这是由于 FK(child table) 上缺少索引引起的。在这种情况下,Oracle 必须在验证约束之前锁定整个子 table。 See AskTom Artice
- SQL 语句顺序错误
在所有情况下,死锁都是由应用程序错误引起的。您将需要来自数据库服务器的死锁报告(.trc 文件)。您会发现涉及 SQL 语句和 tables。由于您使用 Hibernate,您很难预测 SQL 语句执行的顺序,有时它可能有助于扩展实体管理器缓存,以防止过早调用 flush()
.
已编辑: 好的,所以你有 TX(X) 锁。这些是行级别,而 SSX 是 table 级别。那么死锁对象可能是 table 中的一行或索引中的唯一键。跟踪文件还应包含每个会话的先前语句以及游标(SQL 语句执行的位置),游标还应包含绑定变量的值。
尝试执行:
select * from Foo where rowid = 'AAAHGRAB4AAAACBBBo';
select * from Bar where rowid = 'AAAEeXAB4AAADH0BBP';
- table 名字你真的使用 CamelCase 吗?
- "Foo" 和 "Bar" 的 DDL 是什么?
- 在 Foo 和 Bar 之间放下 FK 是否会发生死锁?
- 只调用
em.remove(foo);
时child Bar也会被移除吗?
如果我理解正确 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();