mysql 锁

来源---- 尚硅谷mysql高级

0 并发问题的解决方案

在这里插入图片描述
脏读 需要修改的的这条记录加锁,不让别人读。X锁。
不可重复读 需要读的这条数据加锁,select…for update 加x锁,不让别人改。
在这里插入图片描述

1.读锁 写锁

对于数据库中并发事务的读-读情况并不会引起什么问题.对于写-写、读-写或写-读这些情况可能会引起一-些问题,需要使用MVCC或者加锁的方式来解决它们.在使用加锁的方式解决问题时,由于既要允许读-读情况不受影响,又要使写-写、读-写或写-读情况中的操作相互阻塞,所以mysql实现-一个由两种类型的锁组成的锁系统来解决.这两种类型的锁通常被称为共享锁(shareed lock,S锁)和排他锁(exclusive lock,X锁),也叫读锁(readlock)写锁(writelock)。

1.锁定读

在采用加锁方式解决脏读、不可重复读、 幻读这些问题时,读取一条记录时需要获取该记录的S锁,其实是不严谨的,有时候需要在读取记录时就获取记录的X锁,来禁止别的事务读写该记录,为此MySQL提出了两种比较特殊的SELECT语句格式:
●对读取的记录加S锁:
SELECT … . LOCK IN SHARE MODE ;
#或
SELECT … FOR SHARE; #(8. 0新增语法)

在普通的SELECT语句后边加LOCK IN SHARE MODE ,如果当前事务执行了该语句,那么它会为已经读取到的记录加S锁,这样允许别的事务继续获取这些记录的S锁(比方说别的事务也使用 SELECT …LOCK IN SHAREMODE语句来读取这些记录),但是不能获取这些记录的**X锁(**比如使用 SELECT … FOR UPDATE 语句来读取这些记录或者直接修改这些记录 直接使用update)。如果别的事务想要获取这些记录的X锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的S锁释放掉。

●对读取的记录加X锁:
SELECT … FOR UPDATE;
在普通的SELECT语句后边加FOR UPDATE ,如果当前事务执行了该语句,那么它会为读取到的记录加X锁,这样既不允许别的事务获取这些记录的S锁(比方说别的事务使用 SELECT … LOCK IN SHARE MODE 语句来读取这些记录),也不允许获取这些记录的X锁(比如使用 SELECT … FOR UPDATE 语句来读取这些记录,或者直接修改这些记录)。如果别的事务想要获取这些记录的S锁或者X锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的X锁释放掉

2.写操作

平常所用到的写操作无非是DELETE、UPDATE、 INSERT 这三种:
●DELETE :
对一条记录做DELETE操作的过程其实是先在B+树中定位到这条记录的位置,然后获取这条记录的X锁 ,再执行delete mark 操作。我们也可以把这个定位待删除记录在B+树中位置的过程看成是一个获取 X锁的锁定读。
UPDATE**: 在对一条记录做UPDATE操作时分为三种情况:
不该主键值

  • 情况1:未修改该记录的键值,并且被更新的列占用的存储空间在修改前后未发生变化。

    则先在B+树中定位到这条记录的位置,然后再获取一下记录的 X锁,最后在原记录的位置进行修改操作。我们也可以把这个定位待修改记录在B+树中位置的过程看成是一个获取X锁的锁定读。

  • 情况2:未修改该记录的键值, 并且至少有一-个被更新的列占用的存储空间在修改前后发生 变化。

    则先在B+树中定位到这条记录的位置,然后获取一 - 下记录的X锁,将该记录彻底删除掉(就是把记录彻底移入垃圾链表),最后再插入-条新记录。这个定位待修改记录在B+树中位置的过程看成是一个获取X锁的锁定读,新插入的记录由INSERT操作提供的隐式锁进行保护。
    –修改了主键值。

  • 情况3:修改了该记录的键值,则相当于在原记录上做DELETE操作之后再来一次INSERT操作,加锁操作就需要按照DELETE和INSERT的规则进行了。
    ●INSERT :
    一般情况下, 新插入一条记录的操作并不加锁,通过- -种称之 为隐式锁的结构来保护这条新插入的记录在本事务提交前不被别的事务访问。

2. 表锁

②意向锁

1.意向锁要解决的问题

现在有两个事务,分别是T1和T2,其中T2试图在该表级别上应用共享或排它锁,如果没有意向锁存在,那么T2就需要去检查各个页或行是否存在锁;如果存在意向锁,那么此时就会受到由T1控制的表级别意向锁的阻塞T2在锁定该表前不必检查各个页或行锁而只需检查表上的意向锁。简单来说就是给更大- -级别的空间示意 里面是否已经上过锁。

(t2在表级别添加锁,需要判断表上是否有过其他事务对这个表有过表级别/行级别加过锁。 没有意向锁,需要一行一行判断是否有加锁。
---->引入意向锁 针对一行数据添加x锁后,会自动在上一层中 加一个意向的x锁。
提高效率,否则加表锁,需要一行一行判断是否有加锁。)

在数据表的场景中,如果我们给某一行数据加上了排它锁, 数据库会自动给更大一级的空间,比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了,这样当其他人想要获取数据表排它锁的时候,只需要了解是否有人已经获取了这个数据表的意向排他锁即可。

●如果事务想要获得数据表中某些记录的共享锁,就需要在数据表上添加意向共享锁。
●如果事务想要获得数据表中某些记录的排他锁,就需要在数据表上添加意向排他锁。
这时,意向锁会告诉其他事务已经有人锁定了表中的某些记录。

从上面的案例可以得到如下结论:

  1. InnoDB 支持 多粒度锁 ,特定场景下,行级锁可以与表级锁共存。
  2. 意向锁之间互不排斥,但除了 IS 与 S 兼容外, 意向锁会与 共享锁 / 排他锁 互斥 。
  3. IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突。
  4. 意向锁在保证并发性的前提下,实现了 行锁和表锁共存 且 满足事务隔离性 的要求。

3 行锁

①插入意向锁在这里插入图片描述

在这里插入图片描述
从图中可以看到,由于T1持有gap锁,所以T2和T3需要生成一个插入意向锁的锁结构并且处于等待状态。当T1提交后会把它获取到的锁都释放掉,这样T2和T3就能获取到对应的插入意向锁了(本质上就是把插入意向锁对应锁结构的is_ _waiting属性改为false),T2和T3之间也并不会相互阻塞,它们可以同时获取到id值为8的插入意向锁,然后执行插入操作。事实上插入意向锁并不会阻止别的事务继续获取该记录上任何类型的锁。

② 间隙锁 可能会死锁

gap锁的提出仅仅是为了防止插入幻影记录而提出的。虽然有共享gap锁独占gap锁这样的说法,但是它们起到的作用是相同的。而且如果对一条记录加了 gap锁(不论是共享gap锁还是独占gap锁),并不会限制其他事务对这条记录加记录锁或者继续加gap锁。
在这里插入图片描述
这里session 2并不会被堵住。因为表里并没有id=5这个记录,因此session 1加的是间隙锁(3,8)。 而session 2也是在这个间隙加的间隙锁。它们有共同的目标,即:保护这个间隙,不允许插入值。但,它们之间是不冲突的。

注意,给一条记录加了gap锁只是不允许其他事务往这条记录前边的间隙插新记录,那对于最后一条记录之后的间隙,也就是student 表中id值为20的记录之后的间隙该咋办呢?也就是说给哪条记录加gap锁才能阻止其他事务插入id值在(20,+∞) 这个区间的新记录呢?这时候我们在讲数据页时绍的两条伪记录派上用场了:

Infimum 记录,表示该页面中最小的记录。
Supremum记录,表示该页面中最大的记录。
为了实现阻止其他事务插入id值在(20, +)这个区间的新记录,我们可以给索引中的最后一条记录,也就是id值为20的那条记录所在页面的Supremum记录加上一个gap锁,如图所示。
在这里插入图片描述
间隙锁 — 行级锁 可能会导致死锁。 间隙锁不回冲突,但
在这里插入图片描述
在这里插入图片描述

4 显示锁 隐式锁

隐式锁:确保在insert时,没提交前,不会被其他事务看到。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值