MySQL-锁
锁分类
MySQL中锁按照粒度分,分为以下三类
- 全局锁:锁定数据库中的所有表
- 表级锁:每次操作锁住整张表
- 行级锁:每次操作锁住对应的行数据
全局锁
- 全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞
语法
加全局锁
FLUSH TABLES WITH READ LOCK;
释放锁
UNLOCK TABLES;
数据备份
- 通常,我们为了保障多张表的数据一致性,在数据备份时我们会打开全局锁后进行数据备份,等待数据备份完成后对锁进行释放
MySQL为我们提供了一个数据备份工具mysqldump,我们可以通过以下命令进行数据备份
mysqldump -uroot –proot dbname > xxx.sql
但是数据库开启全局锁时,会导致整体的业务摆停,所以为了解决这个问题,在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份
mysqldump --single-transaction -uroot –proot dbname > xxx.sql
特点
数据库中加全局锁,是一个比较重的操作,存在以下问题
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆
- 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟
表级锁
介绍
- 表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中
分类
对于表级锁分为以下三类
- 表锁
- 元数据锁(Meta Data Lock)
- 意向锁
表锁
对于表锁,分为两类:
- 表共享读锁(read lock)
- 表独享写锁 (write lock)
语法
加锁
LOCK TABLES 表名 ... READ/WRITE
释放锁
UNLOCK TABLES / 客户端释放连接
特点
- 当线程A对表加上读锁时,线程A可以读但是不可以对表进行写操作,不会影响线程B的读但是会阻塞线程B的写,等待线程A释放读锁
- 当线程A对表加上写锁时,线程A可以进行读操作,也可以进行写操作,线程B既不能读也不能写
元数据锁
介绍
- 元数据锁,简称为MDL,作用主要是维护表的元数据的一致性,在表存在未提交的事务时防止表的结构被修改,MDL锁的加锁过程由系统自动控制,无需显示加锁
- 当对一张表执行增删改查操作时会自动添加DML读共享锁,当对表结构进行变更操作的时候,加DML排他锁
常见SQL操作所加的元数据锁
- 当执行SELECT、INSERT、UPDATE、DELETE等语句时,添加的是元数据共享锁(SHARED_READ /SHARED_WRITE),之间是兼容的
- 当执行SELECT语句时,添加的是元数据共享锁(SHARED_READ),会阻塞元数据排他锁(EXCLUSIVE),之间是互斥的
查看数据库中的元数据锁情况
select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks ;
意向锁
- 为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查
- 如何理解意向锁:当行锁存在时对表添加表锁时需要对表进行扫描,确定行锁和表锁不冲突时才可以添加表锁,效率低下,所以在INNODB引擎中引出了表意向锁,它锁住的是整张表,当需要添加表锁时只需要确认表锁和表意向锁不冲突就可以快速添加表锁了,不需要再对表进行扫描
分类
- 意向共享锁(IS): 由语句select ... lock in share mode添加,与表锁共享锁(read)兼容,与表锁排他锁(write)互斥
- 意向排他锁(IX): 由insert、update、delete、select...for update添加,与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥
- 一旦事务提交了,意向共享锁、意向排他锁,都会自动释放
查看意向锁和行锁加锁情况
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;
兼容情况
- 意向共享锁与表读锁是兼容的,与表写锁互斥
- 意向排他锁与表读锁和表写锁都是互斥的
行级锁
介绍
- 行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中
- 注意:InnoDB的数据是基于索引组织的,行锁是通过对
索引上的索引项
加锁来实现的,而不是对记录加的锁
分类
- 行锁(行数据锁 Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持
- 间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持
- 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持
行数据锁
分类:InnoDB实现了以下两种类型的行锁
- 共享锁(S):允许一个事务去读一行,阻止其他事务对该行进行写操作
- 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务对该行进行读和写操作
兼容情况:
- 行共享锁兼容行共享锁,不兼容行排他锁(不允许写操作)
- 行排他锁不兼容行共享锁,也不兼容行排他锁
SQL执行时加行锁情况:
- 默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key(临键) 锁进行搜索和索引扫描,以防止幻读
- 针对唯一索引进行检索时,对已存在的记录进行等值匹配时,将会自动优化为行锁
- InnoDB的行锁是针对于
索引
加的锁,不通过索引条件检索数据,那么InnoDB将对表中的所有记录加锁,此时就会升级为表锁
间隙锁&临键锁
索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁
SELECT ... WHERE ID=XXX LOCK IN SHARE MODE; -- ID不存在
- 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock(临键锁)退化为间隙锁
- 索引上的范围查询(唯一索引),会访问到不满足条件的第一个值为止