MySQL中支持哪些粒度的锁?
在 MySQL 中,锁的粒度(Granularity)指的是被加锁的数据范围大小。MySQL 支持多种粒度的锁,主要取决于所使用的存储引擎以及具体的操作。
按照锁的粒度从大到小划分,MySQL 主要支持以下 4 种粒度的锁:
1. 全局锁(Global Locks)
全局锁是对整个 MySQL 数据库实例加锁。
- 作用范围:整个数据库实例。加锁后,整个数据库处于只读状态,任何对数据的增删改(DML)、表结构更改(DDL)以及更新类事务的提交都会被阻塞。
- 实现命令:
FLUSH TABLES WITH READ LOCK;(简称 FTWRL)。解锁使用UNLOCK TABLES;。 - 主要应用场景:全库逻辑备份(如使用
mysqldump)。为了保证备份时数据的一致性,防止备份过程中有数据写入。 - 注:对于 InnoDB 引擎,由于其支持 MVCC(多版本并发控制),通常在备份时使用
--single-transaction参数来获取一致性视图,从而避免使用全局锁,不影响业务写入。
2. 表级锁(Table-level Locks)
表级锁是对当前操作的整张表加锁。它是 MySQL Server 层和多种存储引擎(如 MyISAM、InnoDB、Memory)都支持的锁。
- 特点:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 分类:
- 普通表锁(Table Lock):
- 读锁(共享锁):
LOCK TABLES t1 READ;(阻塞写,不阻塞读)。 - 写锁(排他锁):
LOCK TABLES t1 WRITE;(阻塞读和写)。
- 读锁(共享锁):
- 元数据锁(MDL, Metadata Lock):
- MySQL 5.5 引入。不需要显式调用,访问表时自动加上。
- 当你对表做 DML 操作(增删改查)时,加 MDL 读锁;对表做 DDL 操作(如改表结构、加索引)时,加 MDL 写锁。它的主要作用是防止 DDL 和 DML 并发导致的数据不一致。
- 意向锁(Intention Lock)(InnoDB 特有):
- 分为意向共享锁(IS)和意向排他锁(IX)。
- 它是 InnoDB 自动加的表级锁。目的是为了在加表锁时,能快速判断表里是否有记录被加了行锁,从而提高加表锁的效率。
- 普通表锁(Table Lock):
3. 行级锁(Row-level Locks)
行级锁是对数据表中的单独一行或多行记录加锁。这是 InnoDB 存储引擎的标志性特性(MyISAM 不支持)。
- 特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度最高。
- 核心机制:InnoDB 的行锁是加在「索引」上的,而不是加在物理行记录上。 如果查询没有命中索引,InnoDB 会退化为锁定表中的所有行(相当于表锁的效果)。
- 分类(根据锁定范围不同):
- 记录锁(Record Lock):仅仅锁住某一条索引记录。例如
WHERE id = 1 FOR UPDATE;(id 是主键或唯一索引)。 - 间隙锁(Gap Lock):锁住两个索引记录之间的间隙,不包括记录本身。主要用于在可重复读(RR)隔离级别下解决幻读问题,防止其他事务在这个间隙插入新数据。
- 临键锁(Next-Key Lock):记录锁 + 间隙锁的组合。既锁住当前记录,又锁住记录前面的间隙。这是 InnoDB 在可重复读(RR)隔离级别下的默认行锁算法。
- 记录锁(Record Lock):仅仅锁住某一条索引记录。例如
4. 页级锁(Page-level Locks)
页级锁是介于表锁和行锁之间的一种锁。
- 作用范围:对一页数据(在 MySQL 中通常一页是 16KB,包含多行记录)进行加锁。
- 特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
- 支持引擎:早期 MySQL 的 BDB (Berkeley DB) 存储引擎支持页级锁,但由于 BDB 引擎目前已被弃用/移除,因此页级锁在现代 MySQL 开发中极少提及,仅作概念了解即可。
总结与对比
| 锁粒度 | 开销与加锁速度 | 死锁情况 | 并发性能 | 支持的存储引擎 | 典型应用场景 |
|---|---|---|---|---|---|
| 全局锁 | 最小,极快 | 不会发生 | 极低 | Server层 | 全库物理/逻辑备份 |
| 表级锁 | 小,快 | 不会发生(普通表锁) | 低 | MyISAM, Memory, InnoDB | 整体表数据的迁移、DDL变更 |
| 页级锁 | 中等 | 会发生 | 中等 | BDB (已弃用) | 历史概念,目前较少使用 |
| 行级锁 | 大,慢 | 会发生 | 极高 | InnoDB | 高并发的在线事务处理 (OLTP) |
💡 核心开发建议:
在使用 InnoDB 时,写操作(UPDATE/DELETE)一定要尽量走唯一索引或普通索引,这样才能精准利用行级锁。如果 WHERE 条件没有命中索引,会导致全表扫描并锁定所有行记录,严重拖垮数据库并发性能。