MySQL 锁
# 介绍
锁是计算机协调多个进程或线程并发访问某一资源的机制。锁保证数据并发访问的一致性、有效性;锁冲突也是影响数据库并发访问性能的一个重要因素。锁是 MySQL 在服务器层和存储引擎层的的并发控制。锁是比较耗资源的。
根据锁的粒度,可以分为:
- 全局锁:锁定数据库中的所有表
- 表级锁:每次操作锁住整张表
- 行级锁:每次操作锁住对应的行数据
每个粒度下根据使用场景又进行细化分类,概览一下:
- 全局锁
- 表级锁
- 表锁
- 元数据锁
- 意向锁
- 行级锁
- 行锁
- 间隙锁
- 临键锁
# 全局锁
全局锁是针对整个数据库实例加锁,加锁后整个实例处理只读状态,后续 DML、DDL 操作都将被阻塞。全库数据备份时为了保证数据一致性可以使用全局锁。
# 语法
加锁:
flush tables with read lock;
释放锁:
unlock tables;
全局锁是一个比较重的操作,存在以下问题:
- 如果在主库上备份,那么备份期间都不能执行更新操作,业务就会停滞
- 如果在从库上备份,那么备份期间从库不能执行主库同步的 Binlog,会导致主从延迟
在 InnoDB 引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份。
# 表级锁
表级锁是在每次操作时锁住整张表,锁定粒度大,发生锁冲突的概率最高,并发度最低。主要分为:
- 表锁
- 元数据锁
- 意向锁
# 表锁
表锁又分为:
- 表共享锁(read lock):A 客户端对指定表加锁,不会影响其它客户端的读,但是会阻塞其它客户端的写
- 表独占锁(write lock):A 客户端对指定表加锁,会阻塞其它客户端的读和写
加锁:
LOCK TABLES
tbl_name [[AS] alias] lock_type
[, tbl_name [[AS] alias] lock_type] ...
lock_type: {
READ [LOCAL]
| [LOW_PRIORITY] WRITE
}
2
3
4
5
6
7
8
LOCAL 修饰符表示可以允许在其他会话中对在当前会话中获取了 READ 锁的的表执行插入。但是当保持锁时,若使用 Server 外的会话来操纵数据库则不能使用 READ LOCAL。另外,对于 InnoDB 表,READ LOCAL 与 READ 相同。也就是说 READ LOAL 仅仅是 MyISAM 类型表才有的功能。
修饰符 LOW_PRIORITY 用于之前版本的 MySQL,它会影响锁定行为,但是从 MySQL 5.6.5 以后,这个修饰符已经被弃用。如果使用它则会产生警告。
释放锁:
UNLOCK TABLES;
# 元数据锁
Meta Data Lock,简写 MDL。加锁过程是系统自动控制。MDL 锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免 DML 与 DDL 冲突,保证读写的正确性。
# 意向锁
为了避免 DML 在执行时,加的行锁与表锁的冲突,在 InnoDB 中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
分类:
- 意向共享锁(IS):由语句 select ... lock in share mode 添加。 与表锁共享锁(read)兼容,与表锁排他锁(write)互斥。
- 意向排他锁(IX):由 insert、update、delete、select...for update 添加。与表锁共享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。
一旦事务提交了,意向共享锁、意向排他锁,都会自动释放。
# 行级锁
行级锁每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在 InnoDB 存储引擎中。InnoDB 的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。分为三类:
- 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行 update 和 delete
- 间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其它事务在这个间隙进行 insert,产生幻读。
- 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙 Gap。
# 行锁
InnoDB 实现了以下两种类型的行锁:
- 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
- 排他锁(X):允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。
常见 SQL 加锁情况:
SQL | 行锁类型 | 说明 |
---|---|---|
INSERT | 排他锁 | 自动加锁 |
UPDATE | 排他锁 | 自动加锁 |
DELETE | 排他锁 | 自动加锁 |
SELECT | 不加锁 | |
SELECT ... LOCK IN SHARE MODE | 共享锁 | 需要手动在 SELECT 之后加 LOCK IN SHARE MODE |
SELECT ... FOR UPDATE | 排他锁 | 需要手动在 SELECT 之后加 FOR UPDATE |
# 间隙锁\临键锁
默认情况下,InnoDB 在 REPEATABLE READ(可重复读)事务隔离级别运行,InnoDB 使用 next-key 锁进行搜索和索引扫描,以防止幻读。
索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁。 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,临键锁退化为间隙锁。 索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。 间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。