深入理解乐观锁和悲剧锁

锁的简介:

在介绍悲观锁和乐观锁之前,我们先来谈一下什么是锁。

锁,在我们生活中随处可见,我们的门上有锁,我们存钱的保险柜上有锁,是用来保护我们财产安全的。

程序中也有锁,当多个线程修改共享变量时,我们可以给修改操作上锁(syncronized).

当多个用户修改表中同一数据时,我们可以给该行数据上锁(行锁)。因此,锁其实是在并发下控制多个操作的顺序执行,以此来保证数据安全的变动。

并且,锁是一种保证数据安全的机制和手段,而并不是特定于某项技术的。悲观锁和乐观锁亦是如此。

在这里插入图片描述

悲剧锁

悲观锁(Pessimistic Concurrency Control),第一眼看到它,相信每个人都会想到这是一个悲观的锁。

那这个悲观体现在什么地方呢?悲观是我们人类一种消极的情绪,对应到锁的悲观情绪,悲观锁认为被它保护的数据是极其不安全的,每时每刻都有可能变动,一个事务拿到悲观锁后(可以理解为一个用户),其他任何事务都不能对该数据进行修改,只能等待锁被释放才可以执行。

数据库中的行锁,表锁,读锁,写锁,以及syncronized实现的锁均为悲观锁。
在这里插入图片描述
我们常用的数据库是mysql,mysql中最常用的引擎是innodb,innodb默认是行锁,而行锁基于索引,因此要想加上行锁,在加锁时必须命中索引,否则将使用表锁。

在这里插入图片描述

乐观锁

与悲观相对应,乐观是我们人类一种积极的情绪。乐观锁(Optimistic Concurrency Control)的“乐观情绪”体现在,它认为数据的变动不会太频繁。因此,它允许多个事务同时对数据进行变动。

但是,乐观不代表不负责,那么怎么去负责多个事务顺序对数据进行修改呢?

乐观锁通常是通过在表中增加一个版本(version)或时间戳(timestamp)来实现,其中,版本最为常用。

事务在从数据库中取数据时,会将该数据的版本也取出来(v1),当事务对数据变动完毕想要将其更新到表中时,会将之前取出的版本v1与数据中最新的版本v2相对比,如果v1=v2,那么说明在数据变动期间,没有其他事务对数据进行修改,此时,就允许事务对表中的数据进行修改,并且修改时version会加1,以此来表明数据已被变动。

如果,v1不等于v2,那么说明数据变动期间,数据被其他事务改动了,此时不允许数据更新到表中,一般的处理办法是通知用户让其重新操作。不同于悲观锁,乐观锁是人为控制的。

在这里插入图片描述

悲观锁解决:

利用悲观锁的解决思路是,我们认为数据修改产生冲突的概率比较大,所以在更新之前,我们显示的对要修改的记录进行加锁,直到自己修改完再释放锁。加锁期间只有自己可以进行读写,其他事务只能读不能写。在这里插入图片描述

乐观锁解决

使用乐观锁的解决思路是,我们认为数据修改产生冲突的概率并不大,多个事务在修改数据的之前先查出版本号,在修改时把当前版本号作为修改条件,只会有一个事务可以修改成功,其他事务则会失败。
在这里插入图片描述

优缺点

悲观锁

优点:悲观锁利用数据库中的锁机制来实现数据变化的顺序执行,这是最有效的办法

缺点:一个事务用悲观锁对数据加锁之后,其他事务将不能对加锁的数据进行除了查询以外的所有操作,如果该事务执行时间很长,那么其他事务将一直等待,那势必影响我们系统的吞吐量。

乐观锁

优点:乐观锁不在数据库上加锁,任何事务都可以对数据进行操作,在更新时才进行校验,这样就避免了悲观锁造成的吞吐量下降的劣势。

缺点:乐观锁因为是通过我们人为实现的,它仅仅适用于我们自己业务中,如果有外来事务插入,那么就可能发生错误。

应用场景

悲观锁:因为悲观锁会影响系统吞吐的性能,所以适合应用在写为居多的场景下。

乐观锁:因为乐观锁就是为了避免悲观锁的弊端出现的,所以适合应用在读为居多的场景下。

猜你喜欢

转载自blog.csdn.net/weixin_46011971/article/details/107416729
今日推荐