首先,相信大家都知道MySQL 有锁机制,通过这些锁机制保证了MySQL的一个读写问题;但是真正能系统的讲出来却是不简单的;
我们先从锁的分类说起吧,网上对于锁的分类可谓是五花八门,有的甚至分成五六类;
今天呢我想讲的就按照最为普遍以及重要的两种分类来展开:按锁的粒度分类以及按锁的性质分类
先从简单的按照性质分类吧
按照锁的性质分类
有共享锁和排他锁
共享锁: 又叫做读锁。 当用户要进行数据的读取时,对数据加上共享锁。共享锁可以同时加上多个。
排他锁: 又叫做写锁。 当用户要进行数据的写入时,对数据加上排他锁。排他锁只可以加一个,他和其他的排他锁,共享锁都相斥
对于这两种锁,有的人喜欢称之为 S 锁 和 X 锁;
下面这张图可以很清晰的表示它们的性质:
按照锁的粒度分类
这里才是我们今天的主角,也就是种类最多最复杂的分类
首先我们可以分为 全局锁 , 表级锁 , 行级锁 , 页面锁(这个很少有人提及,我认为是比较次要的)
全局锁
这个类别比较简单,其实按照名字就可以大概的猜出来,这个锁锁的是一个很大的范围,这里锁的其实就是我们的整个数据库;
加了这个锁之后,我们的数据库就处于一个只读状态,执行非读操作通通会被阻塞;
主要就是应用于我们的数据的备份;
表级锁
表锁
顾名思义,锁住整张表,粒度最大的表级锁了
元数据锁(MDL)
首先说明一下,这个锁一般都是隐式加上的,不需要我们自己去手动加;
因为当我们对数据库表进行操作时(CRUD 或者 更改表结构),会自动给这个表加上 MDL;
其中,CRUD 加的是MDL 读锁,更改表结构加的是MDL 写锁;
意向锁
这个意向锁我们可以理解为是一种flag 锁,起到一个标记的作用;
如果表中的记录被加锁了,我们就可以直接通过这个意向锁来进行判断,而不用去遍历表中的每一行;
自增锁
这个锁算是比较冷门的表级锁了,很多的资料在讲解表级锁的时候,压根就没提到它;
我们都知道,MySQL数据库的有主键自增的策略,这个策略其实是要靠加锁来保证主键唯一且自增的;
InnoDB 存储引擎提供了个 innodb_autoinc_lock_mode 的系统变量,是用来控制选择用自增锁,还是轻量级的锁;
● 当 innodb_autoinc_lock_mode = 0,就采用 自增锁,语句执行结束后才释放锁;
● 当 innodb_autoinc_lock_mode = 2,就采用轻量级锁,申请自增主键后就释放锁,并不需要等语句执行后才释放。
● 当 innodb_autoinc_lock_mode = 1:
○ 普通 insert 语句,自增锁在申请之后就马上释放;
○ 类似 insert … select 这样的批量插入数据的语句,自增锁还是要等语句结束后才被释放
行级锁
这是MySQL中最细腻的一类锁,这也是innodb 特有的,其他引擎并无行级锁这一说;
行锁(记录锁)
这个比较简单,就是单独对某一行加上锁;
间隙锁
这个就和我们之前了解的不太一样,因为它锁的是不存在的行,也就是一个间隙;
假设表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生
(这个锁也是RR 级别下特有的 )
从区间的监督看,这个间隙锁锁的就是左开右开区间:(3,5)
临键锁
这个锁我们可以理解为为是行锁 + 间隙锁
假设,表中有一个范围 id 为(3,5] 的 临键锁,那么其他事务即不能插入 id = 4 记录,也不能修改 id = 5 这条记录
这个间隙锁和临键锁确实是有点类似,我们可以这样更好的理解:
间隙锁锁的是一个范围,不包括记录本身;
临键锁锁的是一个范围 + 记录本身;
这两种锁的引入都是为了解决部分幻读的情况的!
页级锁
页面锁
这个我也不展开说了,页面锁是Berkeley DB存储引擎支持的一种锁粒度,当然,由于BDB引擎被Oracle收购的原因,因此MySQL5.1以后不再直接性的支持该引擎(需自己整合),因此页锁见的也比较少;
一页数据到底是多少呢?其实我也不大清楚,毕竟没用过BDB引擎,但我查阅了网上很多资料,估计就是只一个索引页的大小,即16KB左右