悲观锁和乐观锁机制与数据库事务隔离级别有什么关系?

28

我正在编写一个Web应用程序,两个不同的用户可以更新事项列表,例如待办事项。我已经意识到,乐观锁定机制最适合我,因为我不希望高并发。

我正在研究事务隔离级别,现在有点困惑了。看起来不同的事务隔离级别也可以解决类似的问题。

这两个不同的概念如何相关?如果可能,请给出一个简单的例子。

2个回答

27

这两件事情都涉及数据一致性和并发访问,但它们是两种不同的机制。

锁定防止某个对象被同时访问。例如,当您尝试更新待办事项列表项时,使用悲观锁定数据库会在记录上放置行锁,直到您提交或回滚事务为止,以便不允许其他事务更新相同的记录。乐观锁定是应用程序端检查在获取和尝试更新记录之间时间戳/版本是否已更改。这与事务隔离级别无关。

事务隔离级别是关于读取一致性的。

  • 读未提交级别允许会话看到其他会话的未提交更改
  • 读提交级别允许会话只能看到其他会话已提交的更改
  • 可串行化级别允许会话只能看到在事务开始之前提交的更改

请看下面的例子,我指出了查询结果在事务隔离级别之间的差异。

SESSION 1                                  SESSION 2
--------------------------------           --------------------------------------
SELECT count(*) FROM test;
=> 10
                                           INSERT INTO test VALUES ('x');

SELECT count(*) FROM test;
=> 10 with read committed/serializable
=> 11 with read uncommited (dirty read)
                                           COMMIT;

SELECT count(*) FROM test;
=> 10 with serializable
=> 11 with read uncommitted/read committed

有四种ANSI指定的事务隔离级别(上面的示例中未提到的一种是“可重复读”),除了串行化之外,它们都存在一些异常情况。请注意,这与锁定无关。

您可以在此处查看Oracle文档,其中的概念非常通用。

最后,对于Web应用程序,您使用乐观锁定的方法似乎是合理的。最有可能的情况是,您在两个不同的HTTP请求中获取并更新列表项。在提取后显式锁定记录并保持事务不变是不可能的(您怎么知道第二个请求会到达吗?)乐观锁处理得很好。


ORM提供OCC的方式是通过添加另一列(版本号、时间戳)。如果该行自第一次事务获取后已被其他事务更新(例如,如果版本号与获取时的值不匹配),则会引发异常。这与事务隔离级别无关(除了隔离级别至少应为READ COMMITTED)。我说得对吗? - Abhishek Shukla Ravishankara
我有一个后续问题。任何事务隔离级别是否会阻止或使会话(“会话2”)等待插入值(到表格)/或更新被“会话1”读取的相同数据(行)? - Abhishek Shukla Ravishankara
可能有些引擎(DB2?) - 我不确定。 - Kombajn zbożowy
1
需要注意的一个特殊情况是:如果可序列化事务尝试更新在其开始后被修改并提交的行,则事务将失败 - 不会等待,只会抛出异常并回滚(如果未被捕获)。 - Kombajn zbożowy

0

锁定机制通常用于实现事务隔离级别。因此,事务隔离级别定义了您的事务在并发执行中必须如何行为的合同。锁定机制是实现细节。

从应用程序编写的角度来看,您应该专注于设置适当的事务隔离级别。当然,设置特定的隔离级别意味着锁定,但只要您的应用程序没有承受重负载,您就不需要太关心它。

重要的是,锁定机制在数据库引擎之间有所不同。如果您为一个数据库编写应用程序,并且一段时间后更改了数据库引擎,则您的应用程序可能会表现出不同的行为或某些部分可能需要重新编写。

我在业务应用程序开发方面的建议是不要依赖显式锁定。


我看到像Hibernate、Squeryl这样的ORM提供了乐观并发控制。这是否意味着ORM会为您设置事务隔离级别,并在出现问题时通知您?另外,隔离级别是在数据库级别上设置(对于所有事务都相同),还是我可以通过我的应用程序为每个事务设置任何想要的隔离级别? - Abhishek Shukla Ravishankara
1
这完全是错误的。锁定和事务隔离是不同的机制,有着不同的目的。它们之间没有任何相互关联的含义。 - Kombajn zbożowy
我不认为@Jaroslaw完全是错的。从我所阅读的内容来看,数据库确实采取“悲观”或“乐观”的方法来获取和保持锁定以实现事务隔离级别。但这不是我在问题中所指的。我想我应该写“乐观并发控制”,这可能会使事情更加清晰一些。 - Abhishek Shukla Ravishankara
1
正如我所解释的,事务隔离是关于读取一致性的。大多数数据库(MySQL、PostgreSQL、Oracle)不使用锁来实现这一点。好吧,对于某些数据库可能是这种情况...我不确定,但我认为DB2可以在读取期间放置隐式锁以维护一致性,这也可能取决于隔离级别,但这是一种不常见的方法。 - Kombajn zbożowy

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