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

3
我的朋友描述了一个场景,并向我提出了挑战,让我找到解决方案。他正在使用Oracle数据库和JDBC连接,隔离级别为读提交。在其中一个事务中,他更新了一条记录,执行了一个查询语句并提交了事务。当所有操作都在一个线程中进行时,一切正常。但是当处理多个请求时,会发生死锁。
以下是导致死锁的步骤:
1. 线程A更新记录。 2. 线程B更新另一条记录。 3. 线程A发出select语句并等待线程B的事务完成提交操作。 4. 线程B发出select语句并等待线程A的事务完成提交操作。
由于使用了命令模式,基础框架只允许在所有db操作结束之前仅发出一次提交指令,因此无法在查询语句之前立即发出提交指令。
我的观点是:线程A应该选择已经提交的所有记录,因此不应该发出select语句。但是他说线程A肯定会等待线程B提交记录。这是真的吗?
有哪些方法可以避免上述问题?是否可以更改隔离级别(而不更改底层Java框架)?
关于基础框架的一些信息:它类似于Struts操作,每个请求都由一个操作处理,事务在执行前开始,在执行后提交。

我不理解“等待...事务完成”的含义,因为在Oracle中使用读提交时,读者不会阻塞写者,写者也不会阻塞读者。 - davek
3个回答

4
我相信你的朋友是正确的,如果这些选择是用于更新其他线程已经更新(但尚未提交)的记录。如果它们只是选择数据,并且JDBC框架没有通过强制选择进行更新,则您是正确的。
为避免该问题,请确保仅在真正需要时才选择更新,并在这种情况下,在选择中使用NOWAIT选项。这将导致在操作会阻塞时引发错误。
Oracle将检测死锁并回滚涉及的交易之一。

1

这里开始

Oracle明确支持SQL标准中定义的READ COMMITTED和SERIALIZABLE隔离级别,但这并不是全部。SQL标准试图设置隔离级别,以允许在每个级别执行的查询具有各种一致性程度。REPEATABLE READ是SQL标准声称将从查询中保证读取一致结果的隔离级别。在SQL标准定义中,READ COMMITTED无法提供一致的结果,而READ UNCOMMITTED是获取非阻塞读取的级别。然而,在Oracle数据库中,READ COMMITTED拥有实现读取一致性查询所需的所有属性。在其他数据库中,READ COMMITTED查询可能会返回从未存在于数据库中的答案。此外,Oracle数据库还支持READ UNCOMMITTED的精神。提供脏读的目的是提供非阻塞读取,即查询不会受到相同数据更新的阻塞,也不会阻塞它们。然而,Oracle数据库不需要脏读来实现此目标,也不支持它们。脏读是其他数据库必须使用来提供非阻塞读取的实现方式。READ COMMITTED隔离级别指出事务只能读取已在数据库中提交的数据。没有脏读(读取未提交的数据)。可能会出现不可重复读取(即,在同一事务中重新读取相同行可能会返回不同的答案)和幻读(即,新插入和提交的行变为对查询可见,而在事务早期不可见)。READ COMMITTED可能是数据库应用程序中最常用的隔离级别,在Oracle数据库中它是默认模式。很少看到在Oracle数据库中使用不同的隔离级别。在Oracle数据库中,使用多版本和读取一致性查询,我从ACCOUNTS查询中得到的答案与READ UNCOMMITTED示例中的答案相同。Oracle数据库将修改后的数据重构为查询开始时显示的样子,返回在查询开始时存在于数据库中的答案。

0
在Oracle中,由于写操作不会阻塞读操作,所以不会出现所描述的情况。
但是,以下情况可能会导致死锁:
1. 会话A更新记录#1234。 2. 会话B更新另一条记录#5678。 3. 会话A更新记录#5678。 4. 会话B更新记录#1234。 5. 会话A提交。 6. 会话B提交。
Oracle将检测到死锁并回滚其中一个会话。在传统的客户端/服务器应用程序中,可以通过悲观锁定(SELECT ... FOR UPDATE)来避免这种情况。在Web应用程序中,可以通过使用“乐观锁定列”来避免这种情况,实际上这并不是任何形式的锁定(这就是为什么它避免了死锁,尽管需要大量额外的读取)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接