目录
锁的分类
根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行级锁三类,如图所示:
表级锁的具体细分: 行级锁的具体细分:
一,全局锁
开销和加锁时间界于表级锁和行级锁之间;会出现死锁;锁定粒度界于表级锁和行级锁之间,并发度一般
顾名思义,全局锁就是对整个数据库实例加锁。MySQL提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括 建表、修改表结构等)和更新类事务的提交语句。
全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都select出来存成文本。
二,表级锁
开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
1.表锁
表锁可以分为下面两类:表锁的语法是 lock tables …read/write。与FTWRL类似,可以用unlock tables主动释放锁, 也可以在客户端断开的时候自动释放。需要注意,lock tables语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。
1.1表共享读锁
一旦某个客户端给表加了共享读锁,那么它只能够读不能够写,且不会阻塞其他客户端的读操作而会阻塞其他客户端的写操作,如下图所示:
1.2表独占写锁
一旦某个客户端给表加了写锁,那么它自己能够对该表进行读写操作,但是会阻塞其他客户端的读写操作,如下图所示:
2.元数据锁
MDL(metadata lock)元数据锁,所谓元数据就是描述数据的数据,这里可以理解为表的结构。它不需要显式使用,在访问一个表的时候会被自动加上。MDL的作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个 表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。
因此,在MySQL 5.5版本中引入了MDL,当对一个表做增删改查操作的时候,加MDL读锁(共享锁);当要对表做结构变更操作的时候,加MDL写锁(排他锁)。读锁之间是兼容的,写锁和读锁以及写锁之间是互斥的。如下图所示,只有要改变表结构时才会加上写锁(EXCLUSIVE)。
3.意向锁
为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁。意向锁是一种表锁,锁定的粒度是整张表,分为意向共享锁(IS)和意向排他锁(IX)两类。当有事务给表的数据行加了共享锁或排他锁,同时会给表设置一个标识,代表已经有行锁了,其他事务要想对表加表锁时,就不必逐行判断有没有行锁可能跟表锁冲突了,直接读这个标识就可以确定自己该不该加表锁,这个标识就是意向锁。主要目的是为了提高加表锁的效率。
3.1意向共享锁(IS)
对整个表加共享锁之前,需要先获取到意向共享锁。由语句select...lock in share mode添加。
3.2意向排他锁(IX)
对整个表加排他锁之前,需要先获取到意向排他锁。由insert,update,delete,select...for update添加。
意向锁与其他锁的兼容互斥情况如下图所示,简单来说就是,意向锁之间是兼容的,意向排他锁和除了意向锁之外的锁都是互斥的,排他锁(X)与任何锁都是互斥的。
三,行级锁
开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 行级锁是通过对索引上的索引项加锁来实现的。
1.行锁(record lock)
行锁分为共享锁(S)和排他锁(X)。行锁是锁住单个记录的锁,防止其他事务对其update,delete的操作,在RR和RC隔离级别中都支持。如图所示:
1.1共享锁
共享锁(S)又叫读锁,当一个线程获取读锁后,会阻塞其他用户对该行数据的写操作(即阻止其他事务的排他锁),但不会阻止其他用户对该行数据的读操作(共享锁)
1.2排他锁
排他锁(X)又叫写锁,当一个线程获取写锁之后,会阻塞其他用户对该行数据的读写(update,delete,操作,即其他事务不能够获得该行数据的共享锁与排他锁(获得锁才能对数据进行操作)
对于共享锁大家可能很好理解,就是多个事务只能读数据不能改数据。而排他锁指的是一个事务在一行数据加上排他锁后,其他事务不能再在其上加其他的锁(排他就体现在这里)。mysql InnoDB引擎默认的修改数据语句:update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select …for update语句,加共享锁可以使用select … lock in share mode语句。所以加过排他锁的数据行在其他事务中是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select …from…查询数据,因为普通查询没有任何锁机制。
InnoDB的行锁是通过对索引加的锁,如果不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁。
2.间隙锁(Gap lock)
间隙锁是锁住索引记录之间的间隙,防止其他事务在某个间隙进行insert的操作,产生幻读。在RR隔离级别中都支持。如图所示:
3.临键锁(Next-key lock)
临键锁是行锁和间隙锁的组合,同时锁住数据和数据之间的间隙,在RR的隔离级别中支持。如图所示:
默认情况下,InnoDB在RR的事务隔离级别运行,InnoDB会使用next-key lock锁进行搜索和索引扫描,以防止幻读。
- 索引上的等值查询(唯一索引),给不存在的记录加锁时,优化为间隙锁。
- 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁。
- 索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。
第一点相信大家很容易理解。第二点注意是普通索引,普通索引是非唯一的,所以有可能插入索引值一样的记录。如下图所示,如果我再插入一条索引为18的数据,那么此时会对18-29这段间隙和16-18这段间隙进行加锁(gap lock)。
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
如果觉得对你有帮助,可以给博主点个赞和关注哦~~