【MySQL】InnoDB存储引擎中的锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wrs120/article/details/91046701

  MySQL有关锁的相关知识,参见上篇博客:https://blog.csdn.net/wrs120/article/details/86566879 ,通过上面的介绍,知道InnoDB存储引擎支持行锁,下面主要针对InnoDB存储引擎中的行锁进行讲解:

1. 锁分类

1.1 共享锁(S Lock)

  允许多个事务读同一行数据

1.2 排它锁(X Lock)

  允许事务删除或更新同一行数据

X S
x 不兼容 不兼容
s 不兼容 兼容
  InnoDB支持多粒度锁定,允许事务在行级上的锁和表级上的锁同时存在。为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称为 意向锁。意向锁意味着锁定的对象分为多个层次,意向锁意味着希望在更细粒度上进行加锁,。 意向锁为表级上的锁,但是却表示事务正在读或写某一行记录,而不是整个表,所以意向锁之间不会产生冲突,真正的冲突在加行锁时检查,在给一行记录加锁前,首先要给该表加意向锁。也就是要同时加表意向锁和行锁,分为:
  1. 意向共享锁(IS Lock):事务想获取一张表中的某几行的共享锁
  2. 意向排它锁(IX Lock):事务想获取一张表中的某几行的排它锁

但InnoDB支持的是行级锁,因此意向锁其实不会阻塞除全表扫描意外的任何请求。

IS IX S X
IS 兼容 兼容 兼容 不兼容
IX 兼容 兼容 不兼容 不兼容
S 兼容 不兼容 兼容 不兼容
X 不兼容 不兼容 不兼容 不兼容

1.3 意向锁

为什么会出现意向锁那???
先举个例子:

  1. 事务A锁住了表中的一行,让这一行只能读,不能写。
  2. 之后,事务B申请整个表的写锁。
  3. 如果事务B申请成功,那么理论上它就能修改表中的任意一行,这与A持有的行锁是冲突的。
  4. 数据库需要避免这种冲突,就是说要让B的申请被阻塞,直到A释放了行锁。

数据库要怎么判断这个冲突呢?

  1. step1:判断表是否已被其他事务用表锁锁表
  2. step2:判断表中的每一行是否已被行锁锁住

  注意step2,这样的判断方法效率实在不高,因为需要遍历整个表。于是就有了意向锁。在意向锁存在的情况下,事务A必须先申请表的意向共享锁,成功后再申请一行的行锁。在意向锁存在的情况下,上面的判断可以改成

  1. step1:不变
  2. step2:发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。

  注意:申请意向锁的动作是数据库完成的,就是说,事务A申请一行的行锁的时候,数据库会自动先开始申请表的意向锁,不需要我们程序员使用代码来申请。

总结:为了方便检测表级锁和行级锁之间的冲突,就引入了意向锁


1.4 一致性非锁定读

  指InnoDB通过行多版本控制(MVCC)的方式来读取当前执行时间下 数据库 中的行数据,比如读取的行正进行DELETE或UPDATE操作,此时的读操作不会等待该行上X锁释放,而是会去读取行的某个快照数据,快照数据是通过undo段来完成的,undo数据是事务中用来回滚时候使用的,所以只要执行事务产生就肯定会产生undo数据,使用快照数据是没有额外开销的,同时快照是不会被事务修改的,所以访问这些数据也不用进行加锁操作
  InnoDB对每一行记录可能会有多个版本的快照,因此称之为行多版本技术,这种技术下的并发控制成为多版本并发控制(MVCC)。在READ COMMITTED和REPEATABLE READ事务隔离级别下,InnoDB会使用一致性非锁定读,不过不同的是在READ COMMITTED事务隔离级别下,对快照数据一致性非锁定读总是读取被锁定数据的最新一份快照数据;而在REPEATABLE READ事务隔离级别下,对于快照数据非锁定一致性读总是读取事务开始时候行数据版本


1.5 一致性锁定读

  某些情况下用户需要显式对数据库读取操作进行加锁操作以保证数据逻辑的一致性,这可以通过加锁语句来实现:

SELECT … FOR UPDATE(X锁); SELECT … LOCK IN SHARE MODE(S锁);

这些语句必须在事务中才能使用,在事务提交的时候锁也就自动被释放了。注意的是即使行记录被这样显式锁定了,但其他事务一致性非锁定读也是可以读取某个版本的快照数据的。


1.6 自增长与锁

  对于自增长主键,InnoDB采用一种特殊的表锁机制Auto-inc locking,提高插入的性能,但锁不是在一个事务完成后才释放,而是在完成对自增长值插入的sql后立即释放


1.7 外键与锁

  对于一个外键列,如果没有显式对该列添加索引,则InnoDB会自动对其添加一个索引,这样可以避免表锁。在需要对外键值进行插入或更新就首先需要SELECT父表中的记录,此时的SELECT不能使用一致性非锁定读的方式,因为可能会导致数据不一致的问题,此时实质上使用的是SELECT … LOCK IN SHARE MODE,即会主动加一个S锁防止事务执行中该行被删除或修改,这里也表明对外键添加索引是很有必要性的。


2. 锁的算法

2.1 Record Lock

  行锁,单个行记录上的锁,该算法总会去锁住索引记录,如果InnoDB存储引擎表在建立时没有设置任何一个索引,此时会使用隐式的主键进行锁定

2.2 Gap Lock

  间隙锁,锁定一个范围,但不包含记录本身

2.3 Next-Key Lock

  临建锁,结合了Record Lock和Gap Lock,InnoDB对于行的查询都是采用这种锁,只不过当索引为唯一索引时,Next-Key Lock会降级为Record Lock
Next-Key Lock在REPEATABLE READ事物隔离级别下解决了Phantom Problem幻读问题(Phantom Problem 指在同一事物下,连续执行的两次同样的SQL语句可能导致不同的结果,第二次的SQL可能会返回之前不存在的行),一定注意,幻读是指读取到了不存在的行,而不是同一条数据有了改变,举例说明为什么Next-Key Lock解决了幻读问题,先将事务隔离级别设置为READ COMMITTED:

时间 会话1 会话2
1 set session tx_isolation='READ-OMMITTED'
2 begin;
3 select * from t where a>2 for update 返回 a:4
4 begin;
5 insert into t select 4;
6 commit;
7 select * from t where a>2 for update 返回a:4 a:5 insert into t select 4;
  可以发现事务A出现了幻读,第二次读时多了一条a:5这条数据。但是将事务隔离级别设置为REPEATABLE READ,就不会出现欢度,因为执行select * from where a>2 for update时锁住了(2,+∞),这个范围已经加了x锁,当事务二再执行insert 5时不能获得x锁,只能阻塞。

InnoDB存储引擎中,对于Insert 操作,会检查插入记录的下一条记录是否被锁定,若已经被锁定,则不允许查询。


3. 锁问题

  锁虽然实现了事务的隔离性要求,使得事务可以并发工作,但是存在一定的潜在问题。锁会带来以下三种问题:

3.1 脏读

  读到了其它未提交事务的数据,违反了数据库事务的隔离性要求,此问题将事务隔离级别不设为Read Uncommitted级别就能解决此问题

3.2 不可重复读

  在一个事务内多次读取到同一数据集合,但是读的结果不一样。此问题在Read Committed会出现,将事务隔离级别设为Read Repeatable利用Next-key Lock可解决此问题
脏读是读到未提交的数据,不可重复读是读到已提交的数据,但违反了数据库事务一致性要求

3.3 逻辑上的丢失更新


4. 死锁

4.1 概念

  指两个或两个以上的事务在执行过程中,因争夺资源而造成的的一种互相等待的现象

4.2 检测死锁并解决的办法

  1. 超时:设置一个阈值,等待时间超过这个阈值,其中一个事务就回滚,这样另一个事务就可以继续运行了,但是存在这样一个问题,当超时的事务所占权重比较大,如事务操作更新了很多行,占用了较多的undo log,回滚这个事务的时间相对另一个事务来说时间可能多很多
  2. wait-for graph等待图,检测是否有回路,有则证明有死锁,1.2版本之前采用深度优先算法,1.2死锁检测进行了优化,将递归用非递归的方式实现,回滚undo量最下的事务

InnoDB存储引擎并不会回滚大部分的错误异常,但是死锁除外。当发现死锁后,InnoDB会马上回滚一个事务


5. 锁升级

  SQL SERVER数据库会存在锁升级问题,但是InnoDB存储引擎不存在锁升级问题。因为其不是根据每个记录来产生行锁的,相反,其根据每个事务访问的每个页对锁进行管理,采用位图的方式。一张表有3000000条数据页,每页100条数据,则总共300000000条记录,若此时进行全表更新,袈裟每个页存储的锁信息占30个字节,则所对象仅需90MB的内存,不会产生这种情况,1个锁占用10字节,则所有锁就占用3GB内存

猜你喜欢

转载自blog.csdn.net/wrs120/article/details/91046701