2010-04-03 85 views
2

我的朋友描述了一个场景,并挑战我找到解决方案。他正在使用Oracle数据库和JDBC连接,并将读取提交为事务隔离级别。在其中一个交易中,他更新记录,执行选择语句并提交交易。当一切发生在一个线程内时,事情都很好。但是,当处理多个请求时,发生死锁。JDBC事务死锁:需要解决方案吗?

  1. 线程A更新记录。
  2. 线程B更新另一条记录。
  3. 线程A发出select语句并等待线程B的事务完成提交操作。
  4. 线程B发出select语句并等待线程A的事务完成提交操作。

以上原因导致死锁。由于它们使用命令模式,因此基本框架只允许发出一次提交(在所有数据库操作结束时),因此它们无法在选择语句之前立即发出提交。

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

什么是所有的方式,以避免上述问题?是否有可能改变隔离级别(不改变底层java框架)?

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

+0

我不明白“等待......交易完成”,因为在Oracle读取提交时,读者不会阻止作者和作者不会阻止读者。 – davek 2010-04-03 16:14:06

回答

0

here

甲骨文明确支持,因为他们在 标准正在定义的READ Committed和Serializable隔离 水平。但是,这并不能告诉整个故事。 SQL标准是 尝试设置隔离级别,即 将允许在各个级别在 执行的查询的不同程度的 一致性。 REPEATABLE READ是隔离级别,SQL标准 声称将保证 与查询的读取一致性结果。 在SQL标准定义中,READ COMMITTED不会给您一致的 结果,而READ UNCOMMITTED是 级别用于获取非阻塞式读取。

但是,在Oracle数据库中,READ COMMITTED具有实现读取一致性 查询所需的所有属性 。在其他数据库中,READ COMMITTED查询可以并将返回 数据库中从未存在的答案。此外,Oracle数据库 也支持未读的精神。提供 脏读的目标是提供非阻塞 读取,从而查询不会被 阻止,也不会阻止更新 相同的数据。但是,Oracle数据库 不需要脏读来实现此目标,也不支持它们。 脏读是其他数据库必须使用的实现 提供 非阻塞读取。

阅读 COMMITTED。 READ COMMITTED隔离级别指出 事务可能只读取已在数据库中提交的数据,其中 已被提交。 没有脏读(未读数据的读取为 )。有可能是 不可重复读(即重新读取同一行可能会在同一事务返回不同的 答案 )和 幻像读(即,新插入 和提交的行成为一个 查询间没有”可见t在 之前可见的交易行为)。 READ COMMITTED是 也许是数据库中最常用的 隔离级别 应用程序,它是Oracle Database的默认模式 。很难看到 在Oracle数据库中使用的不同的隔离级别 级别。

在 Oracle数据库,使用 多版本和读一致 查询,我从 账户查询得到的答复是在READ COMMITTED 例如相同的,因为它是在 读取未提交的例子。 Oracle 数据库将重建修改的 数据,因为它在查询 开始时出现,并在查询 开始时返回数据库中的 答案。

3

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

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

Oracle将检测到死锁并回滚相关事务之一。

0

Oracle提供的场景不会发生,原因很简单,写入操作不会阻塞该数据库中的读取操作。

在哪里,我们可以得到一个僵局是在这样的场景:

  1. 会话一个更新记录,#1234。
  2. 会话B更新另一条记录#5678。
  3. 会话A更新记录#5678。
  4. 会话B更新记录#1234。
  5. 会话A发出提交。
  6. 会话B发出提交。

Oracle将检测到死锁并回滚其中一个会话。在传统的客户端/服务器应用程序中,这种情况通过悲观锁定(SELECT ... FOR UPDATE)得以避免。在Web应用程序中,通过使用“乐观锁列”避免了这种情况,它实际上根本没有任何锁定形式(这就是为什么它避免了死锁,尽管是以大量额外的读取为代价)。