MySQL锁分类

        

        有没有觉得MySQL里面的锁有很多?比如排它锁、共享锁、意向锁、插入意向锁、间隙锁、临界锁、记录锁、全局锁锁、表锁、页锁以及行销等。其实,这么多锁存在相互包含的情况,他们是数据库锁不同分类标准导致的。

        锁最终是加在索引上的。

目录

1.锁的分类标准

2.按锁的粒度划分

        2.1 全局锁

        2.2 表级锁

        2.3 页级锁

        2.4 行级锁          

3.按锁的兼容性划分

        3.1共享锁

        3.2排它锁

4.按锁的模式划分

        4.1 记录锁

        4.2 间隙锁

        4.3 临界锁

        4.4 意向锁

        4.5 插入意向锁

        4.6 自增锁


1.锁的分类标准

        锁可以根据 加锁机制、锁粒度、兼容性以及锁模式来区分(直接开抄,原出处不明,因为在太多文章看到了,分不清谁是原创!

MySQL锁分类_第1张图片

MySQL锁分类_第2张图片

MySQL :: MySQL 5.7 Reference Manual :: 14.7.1 InnoDB Locking

2.按锁的粒度划分

        2.1 全局锁

        全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法,命令是

Flush tables with read lock (FTWRL)。

        当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。

           全局锁的典型使用场景是:做全库逻辑备份 。 

   

        2.2 表级锁

         MySQL 里面表级别的锁有两种:一种是表锁,比如自增锁,一种是元数据锁(meta data lock,MDL)。

        表锁是MySQL中最大粒度的锁定机制,会锁定整张表,可以很好的避免死锁,是 MySQL 中最大颗粒度的锁定机制。表锁由 MySQL Server 实现,一般在执行 DDL 语句时会对整个表进行加锁,比如说ALTER TABLE等操作。在执行 DML 语句时,也可以通过LOCK TABLES显式指定对某个表进行加锁。

        2.3 页级锁

        页级锁是 MySQL 中比较独特的一种锁定级别,在其他数据库管理软件中并不常见。页级锁的颗粒度介于行级锁与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力同样也是介于上面二者之间。另外,页级锁和行级锁一样,会发生死锁。页级锁主要应用于 BDB 存储引擎。

        2.4 行级锁          

       MySQL 的行锁是在引擎层由各个引擎自己实现的, 且锁定颗粒度在 MySQL 中是最小的,比如 MyISAM 引擎就不支持行锁,行级锁主要应用于 InnoDB 存储引擎。不支持行锁意味着并发控制只能使用表锁,对于这种引擎的表,同一张表上任何时刻只能有一个更新在执行,这就会影响到并发度,只针对操作的当前行进行加锁,所以行级锁发生锁定资源争用的概率也最小。比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。

        MySQL中3种锁的特性可大致归纳如下:

        MySQL锁分类_第3张图片

3.按锁的兼容性划分

        3.1共享锁

-- 加锁方式:共享锁
SELECT...LOCK IN SHARE MODE

        3.2排它锁

-- 加锁方式:排它锁
SELECT...FOR UPDATE 

InnoDB实现标准的行级锁定,其中有两种类型的锁, 共享锁(S)和排它锁(X)。

  • 共享锁允许持有锁,读取行的事务。

  • 排它锁允许持有锁,更新或删除行的事务。

4.按锁的模式划分

        4.1 记录锁

             记录锁(Record Locks)属于为行锁,表示对某一行记录加锁。

-- id 列为主键列或唯一索引列
SELECT * FROM test WHERE id = 10 FOR UPDATE;

        id为10的行记录会被锁住,可以防止插入,更新或删除行。

        记录锁总是锁定索引记录(SELECT和UPDATE都会加锁),即使表没有定义索引。对于这种情况, InnoDB创建一个隐藏的聚集索引并使用该索引进行记录锁定。但因为可能会扫描全表,那么该锁也就会退化为表锁。

        注意:

  • id列必须为唯一索引或主键列,否则上述语句加的锁会变成临键锁。
  • 查询语句必须为精准匹配=,不能>、<、like等,否则也会退化成临键锁。

        4.2 间隙锁

        间隙锁(Gap Locks)是对索引(非唯一索引)记录之间的间隙,锁定一个区间:加在两个索引之间,或者加在第一个索引之前,或者加在最后一个索引之后的间隙。

        注意!间隙锁锁住的是一个区间,而不仅仅是这个区间中目前仅存在的数据行。

SELECT * FROM test WHERE id BETWEEN 10 and 15 FOR UPDATE;

        例如上面的语句,那(10,15)整个区间的记录行都会被锁住,即id为11,12,13,14数据行的插入操作都会被阻塞,但是10和15两条记录行并不会被锁住。对于唯一索引,如果使用等值查询,那么间隙锁会退化为行锁。

-- 身份证号,唯一索引
SELECT * FROM user WHERE identity_id = 100;

        这里还值得注意的是,间隙锁只阻止其他事务插入到间隙中,并不阻止其他事务在同一个间隙上获得间隙锁,所以 gap x lock 和 gap s lock 有相同的作用,即不同的事务可以在间隙上持有冲突的锁。 例如,事务 A 可以在间隙上持有共享间隙锁(间隙 S 锁),而事务 B 在同一间隙上持有排他间隙锁(间隙 X 锁)。允许冲突间隙锁的原因是,如果从索引中清除记录,则必须合并不同事务在记录上持有的间隙锁。

        总结:

        1.间隙锁锁区间的索引,使用唯一索引搜索唯一行不需要间隙锁定。

        2.在READ COMMITTED(RC)隔离级别下,不会使用gap lock,在RR级别及以上(Serializable)才会使用它。

        3.间隙锁可以共存。一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。共享和排他间隙锁之间没有区别。它们彼此不冲突,并且执行相同的功能。

        

        4.3 临界锁

        临键锁(Next-Key)简单理解是 “记录锁+间隙锁” 的组合,但Next-Key lock与record lock加锁的粒度一样,都是加在一条索引记录上的。一个next-key lock=对应的索引记录的record lock+该索引前面的间隙的gap lock,通过临键锁可以解决幻读的问题。

        默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,在这种情况下,InnoDB使用临键锁进行搜索和索引扫描,以防止幻像行,比如select ... in share mode或者select ... for update语句。但即使你的隔离级别是RR,如果你这是使用普通的select语句,那么InnoDB将是快照读,不会使用任何锁,因而还是无法防止幻读

        记住了,锁住的是索引前面的间隙!比如一个索引包含值,10,11,13和20。那么,临键锁的范围如下,左开右闭:

(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)

        总结:InnoDB在RR事务隔离级别下,在根据非唯一索引对记录行进行UPDATE \ FOR UPDATE \ LOCK IN SHARE MODE 操作时,InnoDB 会获取该记录行的临键锁 ,并同时获取该记录行下一个区间间隙锁。

        4.4 意向锁

        意向锁(Intention Locks)是表级锁,指示事务稍后需要(或想要,表明锁的意向)对表中的行使用哪种类型的锁(共享锁或独占锁),即用来标识该表上面有数据被锁住(或即将被锁)

意向锁有两种类型:

  • 意向共享锁(IS):一个事务在获取(任何一行/或者全表)S锁之前,一定会先在所在的表上加IS锁。

  • 意向排它锁(IX):一个事务在获取(任何一行/或者全表)X锁之前,一定会先在所在的表上加IX锁。

意图锁定协议如下:

  • 在事务可以获取表中行的共享锁之前,它必须首先获取IS表上的锁或更强的锁。

  • 在事务获得表中行的排他锁之前,它必须首先获得IX 表的锁。

表级锁类型兼容性总结在以下矩阵中:

MySQL锁分类_第4张图片

        如果请求事务与现有锁兼容,则向请求事务授予锁,但如果与现有锁冲突,则不会。事务一直等到冲突的现有锁被释放。如果锁定请求与现有锁定发生冲突并且由于会导致死锁而无法授予 ,则会发生错误。

                除了全表请求(例如,LOCK TABLES ... WRITE)之外,意向锁锁不会阻止任何内容。意向锁的主要目的是表明有人正在锁定一行,或者打算锁定表中的一行。

      这里说一下意向锁存在的目的:可以快速判断该表是否存在行锁。

         比如,事务T1,用X锁来锁住了表上的几条记录,那么此时表上存在IX锁,即意向排他锁。那么此时事务T2要进行LOCK TABLE … WRITE的表级别锁的请求,可以直接根据意向锁是否存在而判断是否有锁冲突,就不。

       再比如,事务1在表1上加了S锁后,事务2想要更改某行记录,需要添加IX锁,由于不兼容,所以需要等待S锁释放;如果事务1在表1上加了IS锁,事务2添加的IX锁与IS锁兼容,就可以操作,这就实现了更细粒度的加锁。

        

        4.5 插入意向锁

        插入意向锁(Insert Intention Locks)是在插入一条记录行前,由 INSERT 操作产生的一种特别的间隙锁。

        该锁用以表示插入意向,当多个事务在同一区间插入位置不同的多条数据时,事务之间不需要互相等待。假设存在两条值分别为 4 和 7 的记录,两个不同的事务分别试图插入值为 5 和 6 的两条记录,每个事务在获取插入行上独占的(排他)锁前,都会获取(4,7)之间的间隙锁,但是因为数据行之间并不冲突,所以两个事务之间并不会产生冲突(阻塞等待)。

总结来说,插入意向锁的特性可以分成两部分:

  1. 插入意向锁是一种特殊的间隙锁,如果说间隙锁锁住的是一个区间,那么插入意向锁锁住的就是一个点。

  2. 插入意向锁之间互不排斥,所以即使多个事务在同一区间插入多条记录,只要记录本身(主键、唯一索引)不冲突,那么事务之间就不会出现冲突等待。

        需要强调的是,虽然插入意向锁中含有“意向锁”三个字,但是它并不属于意向锁而属于间隙锁,因为意向锁是表锁而插入意向锁是行锁。

        4.6 自增锁

        自增锁(auto-inc Locks)是一种特殊的表级锁,主要用于事务中插入自增字段(AUTO_INCREAMENT),也就是我们最常用的自增主键id。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务都必须等待插入到该表中,以便第一个事务插入的行接收连续的主键值。

        可以通过配置项 innodb_autoinc_lock_mode调整自增锁算法:传统模式(还没有锁模式这个概念时,InnoDB 的自增锁运行的模式)、连续模式(MySQl 8.0以前的默认模式)以及交叉模式。

        深入剖析 MySQL 自增锁 - 掘金

你可能感兴趣的:(MySQL,mysql,数据库)