我了解乐观锁定和悲观锁定之间的区别。现在有人可以向我解释我什么时候一般使用其中一个?
并根据我是否使用存储过程执行查询而改变了这个问题的答案?
但是仅仅检查一下,乐观的意思是“阅读时不要锁住桌子”,悲观的意思是“阅读时锁住桌子”。
我了解乐观锁定和悲观锁定之间的区别。现在有人可以向我解释我什么时候一般使用其中一个?
并根据我是否使用存储过程执行查询而改变了这个问题的答案?
但是仅仅检查一下,乐观的意思是“阅读时不要锁住桌子”,悲观的意思是“阅读时锁住桌子”。
Answers:
乐观锁定是一种策略,您可以在其中读取记录,记下版本号(执行此操作的其他方法包括日期,时间戳或校验和/哈希),并在写回记录之前检查版本是否未更改。当您将记录写回时,您将过滤该版本的更新以确保它是原子的。(即,在您检查版本并将记录写入磁盘之间,尚未进行更新)和一次单击即可更新版本。
如果记录脏了(即与您的版本不同),则中止事务,用户可以重新启动它。
此策略最适用于不一定要为会话保留与数据库的连接的大容量系统和三层体系结构。在这种情况下,客户端实际上无法维护数据库锁定,因为连接是从池中获取的,并且您可能不会在从一个访问到另一个访问的访问中使用同一连接。
悲观锁定是指您锁定记录以供独占使用,直到完成记录为止。它具有比乐观锁定更好的完整性,但是需要您谨慎设计应用程序,以避免死锁。要使用悲观锁定,您需要直接连接到数据库(在两层客户端服务器应用程序中通常是这种情况)或可以独立于连接使用的外部可用事务ID。
在后一种情况下,您可以使用TxID打开事务,然后使用该ID重新连接。DBMS保持锁定状态,并允许您通过TxID备份会话。这就是使用两阶段提交协议(例如XA或COM + Transactions)的分布式事务的工作方式。
当您不希望发生太多冲突时,将使用乐观锁定。进行正常操作的成本较低,但是如果发生冲突,您将付出更高的代价来解决该事务,因为事务中止了。
当预期发生冲突时,将使用悲观锁定。违反同步的事务将被简单地阻止。
要选择适当的锁定机制,您必须估计读取和写入的数量并做出相应的计划。
乐观假设您在阅读时不会有任何改变。
悲观主义者假设某件事会因此而将其锁定。
如果不一定要完美读取数据,请使用乐观的方法。您可能会读到奇怪的“肮脏”字样-但导致死锁等可能性的可能性要小得多。
大多数Web应用程序都可以进行脏读-在极少数情况下,数据不能完全符合下一次重新加载的要求。
对于精确的数据操作(例如在许多金融交易中),请使用悲观的。准确读取数据且没有未显示的更改非常重要-额外的锁定开销是值得的。
哦,Microsoft SQL Server默认使用页面锁定-基本上是您正在读取的行,并且两侧都有。行锁定更准确,但速度慢得多。通常值得将事务设置为已提交读或未锁定,以避免在读取时出现死锁。
除了已经说过的话:
optimistic
锁定倾向于以牺牲可预测性为代价来提高并发性。Pessimistic
锁定往往会减少并发性,但是更可预测。您付钱等...处理冲突时,有两种选择:
现在,让我们考虑以下丢失更新异常:
丢失更新异常可能发生在已提交读操作中隔离级别中。
在上图中,我们可以看到爱丽丝相信她可以从自己的身上提款40 account
的帐户中但没有意识到Bob刚刚更改了帐户余额,现在该帐户中只剩下20个了。
悲观锁定通过对帐户进行共享或读取锁定来实现此目标,因此可以防止Bob更改帐户。
在上图中,Alice和Bob都将在 account
两个用户都已读取表行。使用“可重复读”或“可序列化”时,数据库将在SQL Server上获取这些锁。
由于Alice和Bob都已读取account
PK值为的1
,因此在一个用户释放读取锁之前,他们都无法更改它。这是因为写操作需要获取写/排他锁,而共享/读锁会阻止写/排他锁。
只有在Alice提交了事务并account
在行上释放了读取锁之后,Bob UPDATE
才会恢复并应用更改。在Alice释放读取锁之前,Bob的UPDATE会阻塞。
有关数据访问框架如何使用底层数据库悲观锁定支持的更多详细信息,请参阅本文。
乐观锁定允许发生冲突,但是在版本更改后应用Alice的UPDATE时会检测到冲突。
这次,我们还有一个version
专栏。version
列每次执行UPDATE或DELETE时都会增加,并且在UPDATE和DELETE语句的WHERE子句中也使用该列。为此,我们需要version
在执行UPDATE或DELETE之前发出SELECT并读取当前值,否则,我们将不知道将哪个版本值传递给WHERE子句或进行递增。
有关数据访问框架如何实现乐观锁定的更多详细信息,请参阅本文。
关系数据库系统已经出现在70年代末80年代初,当时客户通常会通过终端连接到大型机。这就是为什么我们仍然看到数据库系统定义诸如SESSION设置之类的术语的原因。
如今,通过Internet,我们不再在同一数据库事务的上下文中执行读写操作,并且ACID不再足够。
例如,考虑以下用例:
没有乐观锁定,即使数据库事务使用了Serializable,也不会捕获此丢失的更新。这是因为读写是在单独的HTTP请求中执行的,因此是在不同的数据库事务上执行的。
因此,即使在使用包含用户思考时间的应用程序级事务时,乐观锁定也可以帮助您防止丢失更新。
有关应用程序级或逻辑事务的更多详细信息,请参阅本文。
乐观锁定是一种非常有用的技术,即使在使用不太严格的隔离级别(如“读取已提交”)或在后续数据库事务中执行读写操作时,它也可以正常工作。
乐观锁定的缺点是,在捕获到时OptimisticLockException
,数据访问框架将触发回滚,因此会丢失当前正在执行的事务之前我们所做的所有工作。
争用越多,冲突越多,中止交易的机会就越大。由于数据库系统需要还原所有可能涉及表行和索引记录的所有当前挂起的更改,因此回滚对于数据库系统而言可能是昂贵的。
因此,当冲突频繁发生时,悲观锁定可能比较适合,因为它减少了回滚事务的机会。
PESSIMISTIC_FORCE_INCREMENT
。
我会想到悲观锁定将是一个更好的选择的情况。
为了实现乐观锁定,每个数据修改参与者都必须同意使用这种锁定方式。但是,如果有人修改数据而不关心版本列,则将破坏乐观锁定的整个想法。
基本上有两个最受欢迎的答案。在第一个基本上说
乐观需要一种三层体系结构,在这种情况下,您不必为会话维护与数据库的连接,而悲观锁定是指您锁定记录以供独占使用直到完成记录。它比需要直接连接到数据库的乐观锁定要好得多。
乐观(版本化)由于没有锁定而更快,但是(竞争性)锁定在竞争激烈时表现更好,并且最好是阻止工作而不是放弃并重新开始。
要么
发生罕见碰撞时,乐观锁定效果最佳
照原样此页面上。
我创建了答案来解释“保持连接”与“低碰撞”之间的关系。
要了解哪种策略最适合您,请不要考虑数据库具有的每秒事务数,而应考虑单个事务的持续时间。通常,您打开事务,执行操作并关闭事务。这是ANSI考虑到的简短而经典的事务,可以轻松摆脱锁定。但是,您如何实施一个票务预订系统,让许多客户同时预订相同的房间/座位?
您浏览报价,在表格中填写许多可用选项和当前价格。这需要花费很多时间,选项可能会过时,您之间的所有价格都将无效,因此您开始填写表格并按“我同意”按钮,因为您访问的数据没有锁定,而其他人(更敏捷)已经介入更改所有价格,您需要重新设置新价格。
您可以在阅读它们时锁定所有选项。这是悲观的情况。您明白为什么会糟透了。您的系统可以由一个简单地启动预订并开始吸烟的小丑来关闭。在他完成之前,没有人可以保留任何东西。您的现金流量下降到零。这就是为什么在现实中使用乐观的保留。那些磨da时间太久的人必须以更高的价格重新开始预订。
在这种乐观方法中,您必须记录所有已读取的数据(如我的“重复读取”中的数据),并与数据版本一起到达提交点(我想以此报价中显示的价格而非当前价格购买股票)。此时,将创建ANSI事务,该事务将锁定数据库,检查是否进行了任何更改并提交/中止操作。IMO,这是MVCC的有效仿真,它也与Optimistic CC相关联,并且还假定您的事务在中止的情况下重新启动,也就是说,您将进行新的保留。这里的交易涉及人类用户的决定。
我还远远不了解如何手动实施MVCC,但我认为具有重新启动选项的长时间运行的事务是理解该主题的关键。如果我在任何地方都不对,请纠正我。我的回答是由Alex Kuznecov的这一章所激发的。
上面已经说了很多关于乐观和悲观锁定的好东西。需要考虑的重要一点如下:
使用开放式锁定时,我们需要注意以下事实:应用程序将如何从这些故障中恢复。
特别是在异步消息驱动的体系结构中,这可能导致消息处理混乱或更新丢失。
需要仔细考虑失败的情况。