锁定机制(悲观/乐观)如何与数据库事务隔离级别相关?

Posted

技术标签:

【中文标题】锁定机制(悲观/乐观)如何与数据库事务隔离级别相关?【英文标题】:How are locking mechanisms (Pessimistic/Optimistic) related to database transaction isolation levels? 【发布时间】:2014-05-03 23:36:00 【问题描述】:

我正在编写一个 Web 应用程序,其中两个不同的用户可以更新一个事情列表,例如待办事项列表。我已经意识到,乐观锁定机制效果最好,因为我不期望高争用。

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

这两个不同的概念如何相互关联?如果可能的话,举个简单的例子。

【问题讨论】:

【参考方案1】:

这两者都与数据一致性和并发访问有关,但它们是两种不同的机制。

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

事务隔离是关于读取一致性

读取未提交级别允许会话查看其他会话的未提交更改 读取已提交级别仅允许会话查看其他会话已提交的更改 可序列化级别允许会话仅查看在事务开始之前提交的更改

看看下面的例子,我指出了事务隔离级别不同的查询结果。

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 指定的事务隔离级别(上例中没有提到的一个是“可重复读取”),除了可序列化之外,所有这些都受到一些异常的影响。请注意,它与锁定无关。

您可以在here 上查看 Oracle 文档,这些概念非常普遍。

最后,您使用乐观锁定的方法对于 Web 应用程序来说似乎是明智的。很可能您获取一个列表项并在两个不同的 HTTP 请求中更新它。在获取后通过显式锁定记录来保持事务打开是不可能的(或至少是不明智的)(你怎么知道第二个请求是否会到达?)乐观锁定可以优雅地处理这个问题。

【讨论】:

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

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

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

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

我从 15 年的业务应用程序开发中得出的建议是不要依赖显式锁定。

【讨论】:

我看到像 Hibernate、Squeryl 这样的 ORM 提供了乐观并发控制。这是否意味着 ORM 将为您设置事务隔离级别并在出现问题时通知您?此外,隔离级别是在数据库级别设置的(所有事务都相同)还是我可以通过我的应用程序为每个事务设置我想要的任何隔离? 这是大错特错。锁定和事务隔离是不同的机制,服务于不同的目的。一个并不以任何方式暗示另一个。 我不认为@Jaroslaw 是完全错误的。似乎,从我一直在阅读的内容来看,数据库确实采用“悲观”或“乐观”的方法来获取并持有锁来实现事务隔离级别。但这不是我在问题中所指的。我想我应该放“乐观并发控制”,这可能会让事情变得更清楚一些。 正如我所解释的,事务隔离是关于读取一致性的。大多数数据库(mysql、PostgreSQL、Oracle)不使用锁来实现这一点。好的,对于某些 dbs 可能是这种情况......我不确定,但我认为 DB2 可以在读取期间放置隐式锁以保持一致性,它也可能取决于隔离级别,但这是一种不常见的方法。

以上是关于锁定机制(悲观/乐观)如何与数据库事务隔离级别相关?的主要内容,如果未能解决你的问题,请参考以下文章

事物隔离级别_悲观与乐观锁

数据库乐观锁与悲观锁

悲观锁和乐观锁以及事务的隔离级别

深入理解mysql锁与事务隔离级别

事务的隔离级别,乐观锁,悲观锁

第36讲 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景