MySQL | 锁机制下 | 悲观锁 | 乐观锁 | 意向锁 | 间隙锁

目录

一.悲观锁

1.什么是悲观锁

二.乐观锁

1.什么是乐观锁

2.乐观锁的设计方式

三.意向锁(针对于InnoDB存储引擎)

四.间隙锁


一.悲观锁

1.什么是悲观锁

悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。简单的理解为它认为执行的每一步操作都可能出现错误,所以要提前加锁。

二.乐观锁

1.什么是乐观锁

乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。相对悲观锁而言,乐观锁更倾向于开发运用。简单的理解为执行的每一步操作都不可能出现错误,所以它不会主动的加锁,如果要加锁则需要用户操作人员主动的去加锁。

2.乐观锁的设计方式

 版本表示

假设现在test表中有以下数据

test
id name age
001 zhangsan 20

假如A连接进行了以下操作:

select *
from test
where id = 001;

接着在A链接的查询过程中,B连接执行了以下的修改操作

updata test
set age = 18
where id = 001;

此时A连接就有可能读取到脏数据,那么我们怎么解决这个问题呢?在版本表示中,会给数据一个版本号,例如在A连接查询前第一行数据时的版本号为1,在A连接执行查询操作前先查看一下版本号,此时的版本号为1,同样在B连接执行修改操作前也要先查看一下版本号,同样查询到的版本号为1,而当B连接执行完修改操作后,会将版本号改为2,接着轮A连接执行时,还会查询一次版本号,此时A连接查询到的版本号为2,与第一次查询的1不同,因此A连接会发生错误并被释放。

版本表示的这种方式是程序人员来主动处理的。

三.意向锁(针对于InnoDB存储引擎)

先在有一张test表,表中的数据如下:

test
id name age
001 zhangsan 19
002 lisi 20

首先A连接对第一行数据加了一个行锁中的读锁,接着B连接对整张表加一个表锁中的写锁(此时A连接仍在执行,被没有被释放),为了避免A连接添加的行锁与B连接添加的表锁产生冲突,我们就要让B在加表锁时发现表中的第一行数据已经被加了行锁,接着B连接被阻塞,等待A来释放这个行锁。那么B连接怎么判断表中是否存在行锁呢?

首先B连接首先判断表中是否存在表锁,如果不存在则判断表中的每一行是否存在行锁。我们知道判断表中的每一行是否存在行锁,就要遍历整张表,而这种方法的效率太低,因此采用意向锁。即A连接在加行锁之前先加意向读锁(由系统来加),此时整张表只有A连接加的意向锁。接着B连接在加表锁之前,也要先加一个意向写锁,但在此之前表中已经有一个意向读锁了,而这个两个意向锁是互相不兼容的,因此,此时的B连接不能再这张表上加表锁(注意此时B连接可以加意向写锁,但不能加表锁),知道A连接释放后,B连接才能加表锁。

四.间隙锁

间隙锁锁定的是数据间隙

先在有一张test表,表中的数据如下:

test
id name age
1 zhangsan 10

此时A连接进行了以下操作

select *
from test
where id < 1;

insert into test
values (2,lisi,20);

接着B连接到来(此时A连接还没有被释放),执行了以下操作

select *
from test
where id < 5;

我们知道此时A连接还没有执行完毕,因此B连接查询到的结果只有一条数据,而当A连接执行完毕后,B连接又进行了一次相同的查询,但此时查询到的结果却有两条数据,即B事务在同一趟连接中相同的查询却得到了不同的结果,即我面前面说过的幻读问题。那么我们如何使用间隙锁来解决这一问题呢?很简单,B连接在第一次进行查询时,会将id = 1 到 id = 5 之间的间隙锁定,这样A连接在执行插入操作时就无法将数据插入了,而只是将数据暂时的存储在了缓冲区,当B连接释放后,再将缓冲区之间的数据插入到表中。

发布了88 篇原创文章 · 获赞 40 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ThinPikachu/article/details/105579114