锁
2025年12月17日大约 4 分钟
锁
锁是计算机协调多个进程或线程并发访问某资源的机制。
在数据库中,锁用于管理对数据的并发访问,以确保数据的一致性和有效性。
锁的分类
MySQL的锁,按颗粒度分为以下几种类型:
- 全局锁(Global Locks):锁定整个数据库实例,影响所有数据库和表。
- 表级锁(Table Locks):每次操作锁住整张表。
- 行级锁(Row Locks):每次操作锁住对应的行数据。
全局锁
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML写语句、DDL语句、已经更新操作的事务提交语句,都会被阻塞。
- 适用场景:全库逻辑备份,在备份期间,不会因为数据或表结构的更新,而导致备份数据与预期不一致。
- 加全局锁:
FLUSH TABLES WITH READ LOCK; - 解除(释放)全局锁:
UNLOCK TABLES; - 数据库加全局锁后存在的问题:
- 如果数据库在主库备份,那么在备份期间,都不能执行更新操作,业务基本上会处于不可用状态。
- 如果数据库在从库备份,那么在备份期间,从库不能执行主库同步过来的二进制日志(binlog),导致主从延迟。
表级锁
表级锁,即每次操作锁住整张表。锁的粒度大,发生锁冲突的概率高,并发度最低。
- 表级锁的分类
- 表锁
- 元数据锁(Metadata Locks, MDL)
- 意向锁
表锁
- 对于表锁来说,又可以分为两类:
- 读锁(共享锁, S锁, Shared Lock):允许多个事务同时读取数据,但不允许修改数据。
- 写锁(排他锁, X锁, Exclusive Lock):只允许一个事务读取和修改数据,其他事务既不能读取也不能修改数据。
- 语法:
- 加锁:
LOCK TABLES table_name {READ | WRITE}; - 释放锁:
UNLOCK TABLES;
- 加锁:
元数据锁(Meta Data Locks, MDL)
- 元数据锁是数据库表进行操作时,MySQL自动给这个表加的锁。
- MDL读锁:当对表进行CRUD操作时,会加MDL读锁,允许多个事务同时读取数据。
- MDL写锁:当对表进行表结构变更(DDL)操作时,会加MDL写锁,阻塞其他事务对该表的任何操作,直到DDL操作完成。
提示
元数据锁核心,就是为了避免DML和DDL操作冲突,保证数据的一致性。
意向锁
意向锁,是为了避免DML在执行时,加的行锁与表锁冲突,在InnoDB中引入意向锁,使得表锁不用检查每一行的加锁情况,减少表锁的检查。
- 意向共享锁(意向读锁, IS锁):由
SELECT ... lock in share mode添加。 - 意向排他锁(意向写锁, IX锁):由
insert、update、delete、select ... for update等添加。 - 意向锁的兼容情况:
- 意向共享锁(IS):与表锁共享锁(读锁)兼容,不与表锁排他锁(写锁)互斥。
- 意向排他锁(IX):与表锁共享锁(读锁)及排他锁(写锁)都互斥。与意向锁之间不互斥。
- 查看意向锁及行锁加锁情况:
SELECT object_schema, object_name, index_name, lock_type, lock_mode, lock_data FROM performance_schema.data_locks;
行级锁
行级锁,即每次操作锁住对应的行数据。锁的粒度小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。
了解
InnoDB的数据是基于索引组织的,因此行锁实际上是基于索引加锁,而不是对记录加锁。
- 行级锁的分类:
行锁
锁定单个记录行。
- 记录锁的分类:
- 共享锁(S):允许一个事务读取一行数据,但不允许修改数据。通过
SELECT ... LOCK IN SHARE MODE语句加锁。 - 排他锁(X):只允许一个事务读取和修改数据,阻止其他事务获取相同数据集的共享锁和排他锁。通过
SELECT ... FOR UPDATE语句加锁,或者通过INSERT、UPDATE、DELETE语句隐式加锁。
- 共享锁(S):允许一个事务读取一行数据,但不允许修改数据。通过
提示
| 当前锁类型\请求锁类型 | 共享锁(S) | 排他锁(X) |
|---|---|---|
| 共享锁(S) | 兼容 | 冲突 |
| 排他锁(X) | 冲突 | 冲突 |
间隙锁
只存在于RR(可重复读,目的是为了防止其他事务插入间隙,导致幻读。
- 间隙锁可以共存,即多个事务可以同时锁定同一个间隙。
临键锁
锁行锁和间隙锁组合,同时锁住记录和间隙。