我理解乐观锁定和悲观锁定之间的区别。现在,谁能给我解释一下,我一般什么时候使用这两种方法?

这个问题的答案是否会随着我是否使用存储过程来执行查询而变化?

但是为了检查一下,乐观的意思是“阅读时不要锁定表”,而悲观的意思是“阅读时锁定表”。


当前回答

更实际的一点是,在更新分布式系统时,DB中的乐观锁定可能不足以在分布式系统的所有部分之间提供所需的一致性。

例如,在AWS上构建的应用程序中,数据通常同时存在于DB(例如DynamoDB)和存储(例如S3)中。如果一个更新同时涉及DynamoDB和S3, DynamoDB中的乐观锁定仍然可能使S3中的数据不一致。在这种情况下,使用在DynamoDB中持有的悲观锁可能更安全,直到S3更新完成。事实上,AWS为此目的提供了一个锁定库。

其他回答

乐观假设你读的时候什么都不会改变。

悲观的人认为某件事会发生,所以锁定它。

如果数据被完全读取不是必要的,请使用乐观。你可能会得到奇怪的“肮脏”解读——但它不太可能导致死锁或类似的情况。

大多数web应用程序都可以接受脏读——在极少数情况下,下一次重新加载时数据不完全一致。

对于精确的数据操作(如在许多金融交易中)使用悲观。准确读取数据非常重要,没有未显示的更改——额外的锁定开销是值得的。

对了,Microsoft SQL server默认为页面锁定——基本上就是你正在读的那一行和两边的几行。行锁定更准确,但速度要慢得多。通常值得将事务设置为读提交或无锁,以避免读取时发生死锁。

在处理冲突时,你有两种选择:

您可以尝试避免冲突,这就是悲观锁定所做的。 或者,您可以允许冲突发生,但是您需要在提交事务时检测它,这就是乐观锁定所做的。

现在,让我们考虑以下丢失更新异常:

“丢失更新”异常可能发生在“读提交”隔离级别。

在上面的图表中,我们可以看到Alice认为她可以从她的账户中提取40,但没有意识到Bob刚刚改变了账户余额,现在这个账户中只剩下20了。

悲观锁定

悲观锁定通过对帐户使用共享或读锁定来实现这一目标,从而阻止Bob更改帐户。

在上面的图中,Alice和Bob都将获得两个用户都读过的帐户表行上的读锁。当使用可重复读取或可串行化时,数据库在SQL Server上获得这些锁。

因为Alice和Bob都读取了PK值为1的帐户,所以他们都不能更改它,直到一个用户释放读锁。这是因为写操作需要获取写/排他锁,而共享/读锁阻止了写/排他锁。

只有在Alice提交了她的事务并且在帐户行上释放了读锁之后,Bob UPDATE才会恢复并应用更改。在Alice释放读锁之前,Bob的UPDATE会阻塞。

乐观锁定

乐观锁定允许发生冲突,但在应用Alice的UPDATE时检测到它,因为版本已经更改。

这一次,我们有一个额外的版本列。每次执行UPDATE或DELETE时,版本列都会递增,它也用于UPDATE和DELETE语句的WHERE子句中。为此,我们需要发出SELECT并在执行UPDATE或DELETE之前读取当前版本,否则,我们将不知道将哪个版本值传递给WHERE子句或增加哪个版本值。

应用级事务

关系数据库系统出现于70年代末80年代初,当时客户端通常通过终端连接到主机。这就是为什么我们仍然看到数据库系统定义诸如SESSION设置之类的术语。

如今,在Internet上,我们不再在同一个数据库事务的上下文中执行读写操作,ACID也不再足够了。

例如,考虑以下用例:

如果没有乐观锁定,即使数据库事务使用Serializable,也无法捕获这个Lost Update。这是因为读写在不同的HTTP请求中执行,因此在不同的数据库事务上执行。

因此,即使在使用包含用户思考时间的应用程序级事务时,乐观锁定也可以帮助您防止丢失更新。

结论

乐观锁定是一种非常有用的技术,即使在使用不太严格的隔离级别(如Read Committed)或在后续数据库事务中执行读写时,它也能很好地工作。

乐观锁定的缺点是,在捕获OptimisticLockException时,数据访问框架将触发回滚,因此当前正在执行的事务将丢失之前所做的所有工作。

争用越多,冲突就越多,中止事务的机会就越大。回滚对于数据库系统来说代价很高,因为它需要恢复所有当前挂起的更改,这些更改可能涉及表行和索引记录。

因此,当冲突频繁发生时,悲观锁定可能更适合,因为它减少了回滚事务的机会。

我还会想到另外一种情况,悲观锁定会是更好的选择。

对于乐观锁,数据修改的每个参与者都必须同意使用这种锁。但是如果有人修改数据而不考虑版本列,这将破坏乐观锁定的整个思想。

乐观锁定意味着在读取一行时不使用排他锁,因此不会防止丢失更新或写倾斜。所以,使用乐观锁定:

如果没有发生丢失的更新或写倾斜。 或者,即使发生更新丢失或写倾斜也没有问题。

悲观锁定意味着在读取一行时使用排他锁定,从而防止丢失更新或写倾斜。所以,使用悲观锁定:

如果发生更新丢失或写倾斜。 或者出现丢失更新或写倾斜等问题。

在MySQL和PostgreSQL中,SELECT FOR UPDATE可以使用排他锁。

你可以检查我丢失更新的答案,并在MySQL中使用乐观锁定(不使用SELECT FOR update)和悲观锁定(使用SELECT FOR update)编写倾斜示例。

关于乐观锁定和悲观锁定,上面已经说了很多好的东西。 需要考虑的一个重要问题如下:

在使用乐观锁定时,我们需要注意应用程序如何从这些故障中恢复。

特别是在异步消息驱动的体系结构中,这可能导致消息处理无序或更新丢失。

需要仔细考虑失败场景。