MySql 锁及相关知识

一、介绍

  前面介绍了在并发事务下的可能出现的几种问题,脏读、脏写、不可重复读、幻读,几种隔离级别中 RU、RC、RR都是利用 MVCC 解决了脏读、不可重复读,在 RR 下解决了大部分的幻读,而完全解决 幻读 和 脏写 则需要利用 MySql 中的锁机制。

二、锁的几种类别

共享式和独占式:

  共享式 S 锁:在读取一条记录的时候,需要先获取该记录的 S 锁。

  独占式 X 锁:在事务改动一条记录的时候,就需要先获取该记录的 X 锁。

场景:在事务 T1 想要读取 A 记录的时候,获取了 A 记录的 S 锁,此时 T2 也想读取 A 记录,也同样可以获得 A 记录的 S 锁,因为 S 锁是共享的,但此时有 T1 和 T2 两个事务都拥有 A 记录的 S 锁,此时 T3 想要去修改 A 记录,想要去获取 A 记录的 X 锁,需要等 T1 和 T2 都释放其 S 锁,而当 T3 获得 A 记录的 X 锁之后, T4 想要获取 A 记录的 X 锁就需要等 T3 释放其 X 锁。

 

行锁和表锁:

  对一条记录加锁称为行锁,对一个表进行加锁就称为表锁,行锁表锁 同样也可以细分为 共享式独占式,其含义相同。

场景:有表 F,表F中一行数据 A ,当 T1 线程获取 A 共享式锁 S ,此时其他线程可以获取 A 的共享式锁S,但不能获取 A 的独占式锁 X ,那如果此时 T2 线程想获取表 F 的共享式锁也是可以的,但如果 T3 想获取表 F 的独占式锁,就需要等待表 F 中所有锁都释放了,但如何检测这张表中有没有其他锁呢?遍历?太耗费时间,为了解决这个问题,就出现了意向锁。

意向锁也可以分为:意向共享锁、意向独占锁。

当一个事务想要去获取表中数据的共享锁或者独占锁的时候,就先在表上丢一个意向锁,表示我将要去获取里面的锁,这样如果一个线程想要获得表的独占锁的时候,可以先看表上面有没有意向锁,如果没有,说明没有表里面是没有锁的,则可以获取这个表的独占锁,而不需要遍历整张表

三、InnoDB中的锁

表级别锁 AUTO-INC 锁:

 

行级别锁:

1、Record Lock

  (正经记录锁)有 X 锁和 S 锁之分,单行数据可以施加的锁。

2、Gap Lock

  前面知道,在 RR 隔离级别下能解决大部分幻读的情况,解决方案可以用 MVCC 解决,也可以用锁解决。

但当用锁解决的时候,我们如果用 Record Lock 去给数据加锁,但出现幻读的时候,我想加锁的时候那些数据根本不存在,所以这样是行不通的,所以就出现了 Gap 锁。

 

在前面知道,聚簇索引的主键都是递增排列的,而我们读取的多条数据的主键肯定在某个范围,这里 Gap 锁的含义就是可以阻止某个范围内有数据插入,如果有数据想插入我正在读取的范围内,这个事务就会被阻塞,等我读完后才会继续,这样就有效的阻止了幻读的产生。

3、Next - Key - Lock

  有时候我们锁住某条记录,有想该记录与前面的间隙之间没有数据插入,这时候就有了 Next - Key - Lock ,这个锁本质就是 Gap 锁 和 Record 锁的组合。

4、Insert Intention Lock

  在事务插入一条数据时,首先得判断插入位置是否有 Gap 锁,如果有则需要等待。但 InnoDB 规定,事务在等待的时候也需要在内存中生成一个锁结构,表明有事务想要在某个间隙插入新纪录,但此时在等待状态,这是 Insert Intention Lock(插入意向锁)。

5、隐式锁

  在内存中插入并维护一个锁并不是一件零成本的事情,出于节省的考虑,InnoDB 提出隐式锁。

场景:一个事务首先插入(注意)了一条记录(此时并没有与该记录相关联的锁结构),然后另一个事务立刻读取这条记录(获取这个记录的 S 锁)或者 (获取这条记录的 X 锁),此时前面一个没有锁的事务还没有提交,如果允许这种情况发生,就出现了脏读和脏写。这里就用到了隐式锁。

  而要实现这个隐式锁,还需要借助之前提到的 事务ID,新插入的记录会记录其进行插入的事务ID,如果其他事务访问到此时的新纪录的时候,发现其事务ID还在活跃列表,就说明此时的插入事务还没有提交,就帮其创建一个 X 锁,然后自己进入等待状态,这个事务 ID 就充当了一个隐式锁的功能

 

为什么隔离级别 RR 只能避免大部分幻读?

  假设有表 F ,T1 第一次执行普通查询的时候,生成了 ReadView(见MVCC原理),之后 T2 向 F 表新插入一个记录并提交(注意这个提交),之后 ReadView 并不能阻止 T1 对这条新数据进行修改(因为T2已经提交),如果执行修改后,这条数据的版本链就变成了 T1,之后如果 T1 再进行读取,就会读取到这条数据,也就可以把这条记录返回,也就出现了幻读,因为这种情况,所以 MVCC并不能阻止全部幻读。

 

posted @ 2022-03-25 16:24  空心小木头  阅读(46)  评论(0编辑  收藏  举报