锁定机制(悲观/乐观)如何与数据库事务隔离级别相关?
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 可以在读取期间放置隐式锁以保持一致性,它也可能取决于隔离级别,但这是一种不常见的方法。以上是关于锁定机制(悲观/乐观)如何与数据库事务隔离级别相关?的主要内容,如果未能解决你的问题,请参考以下文章