我理解乐观锁定和悲观锁定之间的区别。现在,谁能给我解释一下,我一般什么时候使用这两种方法?
这个问题的答案是否会随着我是否使用存储过程来执行查询而变化?
但是为了检查一下,乐观的意思是“阅读时不要锁定表”,而悲观的意思是“阅读时锁定表”。
我理解乐观锁定和悲观锁定之间的区别。现在,谁能给我解释一下,我一般什么时候使用这两种方法?
这个问题的答案是否会随着我是否使用存储过程来执行查询而变化?
但是为了检查一下,乐观的意思是“阅读时不要锁定表”,而悲观的意思是“阅读时锁定表”。
当前回答
乐观锁定的一个用例是让应用程序使用数据库允许其中一个线程/主机“声明”任务。这是一个经常为我派上用场的技巧。
我能想到的最好的例子是使用数据库实现的任务队列,多个线程同时声明任务。如果一个任务有状态'Available', 'Claimed', 'Completed', db查询可以这样说:Set status='Claimed' where status='Available'。如果多个线程试图以这种方式改变状态,那么除了第一个线程之外,其他线程都会因为脏数据而失败。
注意,这是一个只涉及乐观锁定的用例。因此,作为“乐观锁定用于不期望有太多冲突的情况”的替代说法,它也可以用于您期望有冲突但只希望一个事务成功的情况。
其他回答
乐观锁定用于不期望发生太多冲突的情况。进行正常操作的成本较低,但如果碰撞确实发生,您将支付更高的代价来解决它,因为交易被中止。
悲观锁定在预期发生碰撞时使用。会违反同步的事务被简单地阻塞。
为了选择合适的锁定机制,您必须估计读取和写入的量并相应地进行计划。
乐观锁定和悲观锁定是数据库中锁定数据的两种模型。
乐观锁定:仅在向数据库提交更改时才锁定记录。
悲观锁定:在编辑记录时锁定记录。
注意:在两种数据锁定模型中,锁都是在将更改提交给数据库后释放的。
在处理冲突时,你有两种选择:
您可以尝试避免冲突,这就是悲观锁定所做的。 或者,您可以允许冲突发生,但是您需要在提交事务时检测它,这就是乐观锁定所做的。
现在,让我们考虑以下丢失更新异常:
“丢失更新”异常可能发生在“读提交”隔离级别。
在上面的图表中,我们可以看到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时,数据访问框架将触发回滚,因此当前正在执行的事务将丢失之前所做的所有工作。
争用越多,冲突就越多,中止事务的机会就越大。回滚对于数据库系统来说代价很高,因为它需要恢复所有当前挂起的更改,这些更改可能涉及表行和索引记录。
因此,当冲突频繁发生时,悲观锁定可能更适合,因为它减少了回滚事务的机会。
乐观假设你读的时候什么都不会改变。
悲观的人认为某件事会发生,所以锁定它。
如果数据被完全读取不是必要的,请使用乐观。你可能会得到奇怪的“肮脏”解读——但它不太可能导致死锁或类似的情况。
大多数web应用程序都可以接受脏读——在极少数情况下,下一次重新加载时数据不完全一致。
对于精确的数据操作(如在许多金融交易中)使用悲观。准确读取数据非常重要,没有未显示的更改——额外的锁定开销是值得的。
对了,Microsoft SQL server默认为页面锁定——基本上就是你正在读的那一行和两边的几行。行锁定更准确,但速度要慢得多。通常值得将事务设置为读提交或无锁,以避免读取时发生死锁。
更实际的一点是,在更新分布式系统时,DB中的乐观锁定可能不足以在分布式系统的所有部分之间提供所需的一致性。
例如,在AWS上构建的应用程序中,数据通常同时存在于DB(例如DynamoDB)和存储(例如S3)中。如果一个更新同时涉及DynamoDB和S3, DynamoDB中的乐观锁定仍然可能使S3中的数据不一致。在这种情况下,使用在DynamoDB中持有的悲观锁可能更安全,直到S3更新完成。事实上,AWS为此目的提供了一个锁定库。