JDBC事务死锁:需要解决方案吗?

时间:2010-04-03 15:28:51

标签: java oracle jdbc transactions deadlock

我的朋友描述了一个场景并挑战我找到解决方案。他使用Oracle数据库和JDBC连接,并将read committed作为事务隔离级别。在其中一个事务中,他更新记录,执行select语句并提交事务。当一切都发生在一个线程中时,事情就好了。但是当处理多个请求时,会发生死锁。

  1. Thread-A更新记录。
  2. 主题B更新另一条记录。
  3. Thread-A发出select语句并等待Thread-B的事务完成提交操作。
  4. Thread-B发出select语句并等待Thread-A的事务完成提交操作。
  5. 以上导致死锁。由于它们使用命令模式,因此基本框架仅允许发出一次提交(在所有数据库操作结束时),因此它们无法在select语句之前立即发出提交。

    我的论点是:Thread-A应该选择所有提交的记录,因此不应该发布。但他说Thread-A肯定会等到Thread-B提交记录。这是真的吗?

    有什么方法可以避免上述问题?是否可以更改隔离级别(不更改底层的Java框架)?

    关于基本框架的信息很少:它类似于Struts动作,每个请求由一个动作处理,事务在执行之前开始,在执行之后提交。

3 个答案:

答案 0 :(得分:4)

我相信你的朋友是正确的如果选择是为了更新其他线程已经更新(但尚未提交)的记录。如果他们只是简单地选择数据,并且JDBC框架没有通过强制选择更新来帮助您,那么您是正确的。

要避免此问题,请确保在真正需要时只选择更新,在这种情况下,请使用select中的NOWAIT选项。如果操作阻止,这将导致引发错误。

Oracle将检测死锁并回滚其中一个事务。

答案 1 :(得分:1)

来自here

  

Oracle明确支持READ   承诺和SERIALIZABLE隔离   他们在中定义的等级   标准。但是,这并不能说明问题   整个故事。 SQL标准是   试图设置隔离级别   会允许不同程度的   执行查询的一致性   每个级别。 REPEATABLE READ是   隔离级别即SQL标准   索赔将保证   查询的读取一致结果。   在SQL标准定义中,READ   COMMITTED不会给你一致的   结果,和READ UNCOMMITTED是   用于获取非阻塞读取的级别。

     

但是,在Oracle数据库中,请阅读   COMMITTED具有所有属性   要求达到读一致性   查询。在其他数据库中,READ   COMMITTED查询可以并将返回   答案从未存在过   数据库。而且,Oracle数据库   也支持READ的精神   未提交。提供的目标   脏读是为了提供非阻塞   读取,从而不阻止查询   通过,而不是阻止,更新   相同的数据。但是,Oracle数据库   不需要脏读来实现   这个目标,也不支持他们。   脏读是一种实现   其他数据库必须使用提供   非阻塞读取。

     

READ   提交。 READ COMMITTED   隔离级别表明a   事务可能只读取数据   已经在数据库中提交。   没有脏读(读取   未提交的数据)。可能有   不可重复的读取(即重读   相同行可能会返回不同的行   在同一交易中回答)和   幻像读取(即新插入   和提交的行变得可见   之前看不到的查询   交易)。 READ COMMITTED是   也许是最常用的   数据库中的隔离级别   各地的应用程序,它是   Oracle数据库的默认模式。它的   很少看到不同的隔离   Oracle数据库中使用的级别。

     

在   使用Oracle数据库   多版本控制和读取一致性   查询,我得到的答案   ACCOUNTS查询在READ中是相同的   提交的例子就像它在   READ UNCOMMITTED示例。神谕   数据库将重建修改后的内容   查询时出现的数据   开始了,回答了那个答案   在数据库中查询时   启动。

答案 2 :(得分:0)

所呈现的场景不会在Oracle中发生,原因很简单,因为写入不会阻止该数据库中的读取。

在这种情况下我们可以陷入僵局:

  1. 会话A更新记录#1234。
  2. 会话B更新另一条记录#5678。
  3. 会话A更新记录,#5678。
  4. 会话B更新记录#1234。
  5. 会话A发出提交。
  6. 会话B发出提交。
  7. Oracle将检测死锁并回滚其中一个Sessions。在传统的客户端/服务器应用程序中,悲观锁定(SELECT ... FOR UPDATE)可以避免这种情况。在Web应用程序中,通过使用“乐观锁定列”来避免这种情况,“乐观锁定列”实际上根本没有锁定形式(这就是为什么它避免了死锁,尽管以大量额外读取为代价)。