MySQL的锁机制
从阻塞角度来看
MySQL锁分为共享锁和排他锁,也可以叫读锁和写锁
1.全局锁
对整个数据库进行加锁,加锁后整个实例都处于只读状态 ,典型应用场景就是做全库的逻辑备份,对所有表进行锁定,获取一致性视图,保证数据的完整性
演示:
-- 锁住
flush tables with read lock;
-- 备份 此命令不是sql语句的,在控制台输入
mysqldump -uroot -proot xxx > xxx.sql;
-- 解锁
unlock tables;
弊端:
阻塞太大,比较重的操作,会阻塞更新语句
2.表级锁
每次操作锁住整张表,发生锁冲突的概率高,主要分为表锁;元数据锁MDL,意向锁三类
2.1表锁
对于表锁分为表共享读锁,表独占写锁
-- 加锁
lock tables 表名 .. read/write
-- 释放锁
unlock tabless/客户端断开连接
2.2元数据(表结构)锁 MDL
MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上,MDL锁主要作用是维护元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作
在MySQL5.5中引入了MDL,当对一张表进行增删改查时,加MDL读锁(共享锁),当对表结构进行变更操作的时候,加MDL写锁(排他锁)。
2.3意向锁
如果没有意向锁,如果线程A对表加了行锁,线程B进来相加一个表锁,那他得去先去遍历整个表,找找有没有其他锁,效率就很低
为了避免DML在执行时,加的行锁与表锁冲突,在InnoDB中引入了意向锁,使得表锁不用检查每一行数据是否加锁,使用意向锁来减少表的锁的检查。
当线程A执行DML时,会加行锁同时对整张表加意向锁
这时候线程B进来想加锁就会检查是否与意向锁兼容,不兼容就会被阻塞
3.行级锁
每次加锁对应的行数据,发生锁冲突的概率最低,InnoDB引擎支持,InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现,而不是对记录加的锁
行级锁分为三类:
- 行锁:锁住单行记录,防止其他事务更新,RC,RR隔离级别下支持
- 间隙锁:锁定索引记录间隙,确保索引记录间隙不变,防止其他事务在这个间隙insert,产生幻读
- 临键锁:行锁和间隙锁的组合,同时锁住数据和数据前边的间隙
3.1行锁
InnoDB实现了两种类型的行锁,共享锁和排他锁,锁住的是一行数据。
InnoDB的行锁是针对索引加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时会升级为表锁
3.2间隙锁
- 索引上等值查询(唯一索引),给不存在的记录加锁,会优化为间隙锁,当其他事务想插入数据到该不存在的值所在的间隙时(即B+树叶子节点遍历到比要查的值的下一个值之前的间隙),会被阻塞,防止幻读
- 索引上的等值查询(普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock退化为间隙锁(对于这种普通索引,不唯一,需要锁住这一行,这一行之前之后的间隙都需要锁住,才能保证不幻读)
- 索引上的范围查询(唯一索引),会访问到不满足条件的第一个值为止,之间的间隙和行记录都会被加锁
间隙锁的目的就是防止其他事务插入间隙,间隙锁可以共存,一个事务采用间隙锁不会影响别的事务在同一个位置插入间隙锁