Appearance
锁详解
一、按锁的粒度划分(最核心分类)
锁的粒度决定了锁定的范围,从大到小依次为:全局锁 → 表级锁 → 行级锁 → 页级锁(几乎不用)。
1. 全局锁(Global Lock)
定义:锁定整个MySQL实例,所有数据库的所有表均变为只读状态(无法执行INSERT、UPDATE、DELETE、ALTER TABLE等写操作)。
作用:用于全库逻辑备份(如mysqldump),确保备份期间数据一致性(避免备份过程中数据被修改)。
常见方式:
FLUSH TABLES WITH READ LOCK (FTWRL):强制关闭所有打开的表,并对所有表加读锁,直到执行UNLOCK TABLES释放。- 优点:比
SET GLOBAL read_only=1更安全(read_only对超级用户无效,FTWRL对所有用户有效)。
- 优点:比
SET GLOBAL read_only = 1:设置全局只读,但超级用户仍可写,一般不用于备份。
注意:全局锁会阻塞所有写操作,对业务影响大,建议用InnoDB的--single-transaction选项(通过MVCC实现一致性备份,无需全局锁)。
2. 表级锁(Table-Level Lock)
定义:锁定整个表,是MyISAM存储引擎的默认锁机制(InnoDB也支持,但更常用行级锁)。
分类:
(1)表共享读锁(Table Shared Read Lock, S锁)
- 作用:允许多个会话同时持有,允许读,禁止写(包括当前会话)。
- 语法:
LOCK TABLES t READ; - 兼容性:与其他S锁兼容,与X锁冲突。
(2)表排他写锁(Table Exclusive Write Lock, X锁)
- 作用:仅允许一个会话持有,允许读写,禁止其他会话读或写。
- 语法:
LOCK TABLES t WRITE; - 兼容性:与所有锁冲突(包括自身)。
(3)意向锁(Intention Lock)
- 定义:InnoDB为协调表级锁与行级锁而引入的“标记锁”,自动添加(无需手动操作)。
- 作用:当需要加表级锁时,无需扫描全表检查每行的锁状态,只需检查意向锁即可,减少锁检查的开销。
- 分类:
- 意向共享锁(Intention Shared Lock, IS锁):加行S锁前自动添加(如
SELECT ... FOR SHARE)。 - 意向排他锁(Intention Exclusive Lock, IX锁):加行X锁前自动添加(如
INSERT/UPDATE/DELETE)。
- 意向共享锁(Intention Shared Lock, IS锁):加行S锁前自动添加(如
- 兼容性(关键):
请求锁\持有锁 IS锁 IX锁 S锁 X锁 IS锁 兼容 兼容 兼容 冲突 IX锁 兼容 兼容 兼容 冲突 S锁 兼容 兼容 兼容 冲突 X锁 冲突 冲突 冲突 冲突 例如:若会话A持有表的IX锁(行X锁的意向),会话B要加表S锁(读全表),允许(IX与S兼容);但会话B要加表X锁(写全表),禁止(IX与X冲突)。
(4)元数据锁(Metadata Lock, MDL)
- 定义:保护表的元数据(如表结构、列定义),自动添加(无需手动操作)。
- 作用:防止并发修改表结构与数据操作冲突(如一个会话在SELECT,另一个会话在ALTER TABLE,会阻塞直到SELECT完成)。
- 分类:
- MDL读锁(共享):执行
SELECT、SHOW TABLES等操作时添加,允许多个会话持有。 - MDL写锁(排他):执行
ALTER TABLE、DROP TABLE、RENAME TABLE等操作时添加,仅允许一个会话持有。
- MDL读锁(共享):执行
- 注意:MDL锁的释放时机取决于事务提交(如
BEGIN; SELECT * FROM t;会持有MDL读锁直到COMMIT),长时间未提交的事务会阻塞表结构修改。
(5)自增锁(AUTO-INC Lock)
- 定义:用于自增列(AUTO_INCREMENT)的生成,确保自增值的唯一性和连续性。
- 作用:插入数据时,对自增列加锁,防止多个会话生成重复的自增值。
- 模式(由
innodb_autoinc_lock_mode参数控制):- 0(传统模式):表级锁,锁定整个表直到插入完成,自增值连续,但性能低(适合批量插入)。
- 1(连续模式,默认):轻量级锁,对于简单插入(如
INSERT INTO t VALUES (NULL))用行级锁,自增值连续;对于批量插入(如INSERT INTO t SELECT ...)用表级锁。 - 2(交错模式):行级锁,允许并发插入,自增值可能不连续(如会话A插入id=1,会话B插入id=3),但性能最高(适合高并发插入)。
(6)表空间锁(Tablespace Lock)
- 定义:锁定表空间文件(如
.ibd文件),用于ALTER TABLE ... DISCARD TABLESPACE或IMPORT TABLESPACE等操作,防止其他会话访问该表空间。
3. 行级锁(Row-Level Lock)
定义:锁定单行记录,是InnoDB存储引擎的核心特性(MyISAM不支持),基于索引实现(无索引时会升级为表级锁)。
作用:最小化锁冲突,提高并发性能(如多个会话可同时修改不同行)。
分类:
(1)记录锁(Record Lock)
- 定义:锁定单行具体记录,基于主键或唯一索引(若用非唯一索引,会升级为临键锁)。
- 语法:
SELECT * FROM t WHERE id=1 FOR UPDATE;(加行X锁);SELECT * FROM t WHERE id=1 FOR SHARE;(加行S锁)。 - 作用:防止其他会话修改或删除该记录。
(2)间隙锁(Gap Lock)
- 定义:锁定索引之间的间隙(不包括记录本身),用于防止幻读(Repeatable Read(RR)隔离级别下有效)。
- 示例:若表t的id索引值为1、3、5,间隙为
(-∞,1)、(1,3)、(3,5)、(5,+∞)。执行SELECT * FROM t WHERE id BETWEEN 1 AND 5 FOR UPDATE;会锁定这些间隙,防止插入id=2、4等数据。 - 注意:间隙锁仅在RR级别下生效(Read Committed(RC)级别下无间隙锁)。
(3)临键锁(Next-Key Lock)
- 定义:记录锁+间隙锁(锁定当前记录及前面的间隙),是InnoDBRR级别下的默认行锁方式。
- 示例:若id索引值为1、3、5,临键锁为
(-∞,1]、(1,3]、(3,5]、(5,+∞)(]表示包含当前记录)。执行SELECT * FROM t WHERE id=3 FOR UPDATE;会锁定(1,3]间隙,防止插入id=2的数据。 - 作用:彻底解决幻读(通过锁定记录及间隙,避免插入新数据)。
(4)插入意向锁(Insert Intention Lock)
- 定义:插入数据时,对目标间隙加的意向锁(属于间隙锁的子类)。
- 作用:允许并发插入不同位置的记录,减少锁冲突。
- 示例:会话A要插入id=2(间隙
(1,3)),会话B要插入id=4(间隙(3,5)),两者的插入意向锁兼容,可同时执行;但如果两者都插入id=2,会因记录锁冲突而阻塞。
(5)谓词锁(Predicate Lock)
- 定义:在Serializable隔离级别下,锁定满足某个条件的所有行(如
SELECT * FROM t WHERE name LIKE 'a%' FOR UPDATE;),用于防止幻读。 - 注意:InnoDB并未直接实现谓词锁,而是用临键锁覆盖谓词条件的范围(如
name LIKE 'a%'会锁定所有name以a开头的行及间隙)。
4. 页级锁(Page-Level Lock)
定义:锁定一页数据(MySQL默认页大小为16KB),是BDB存储引擎的默认锁机制(几乎不用)。
特点:粒度介于表级锁与行级锁之间,并发性能优于表级锁,但低于行级锁。
二、按锁的类型划分
1. 共享锁(Shared Lock, S锁)
- 定义:允许多个会话同时持有,允许读,禁止写。
- 语法:
SELECT ... FOR SHARE;(InnoDB);LOCK TABLES t READ;(表级)。 - 兼容性:与S锁兼容,与X锁冲突。
2. 排他锁(Exclusive Lock, X锁)
- 定义:仅允许一个会话持有,允许读写,禁止其他会话读或写。
- 语法:
SELECT ... FOR UPDATE;(InnoDB);LOCK TABLES t WRITE;(表级);INSERT/UPDATE/DELETE(自动加X锁)。 - 兼容性:与所有锁冲突。
三、按锁的实现方式划分
1. 悲观锁(Pessimistic Lock)
- 定义:假设并发冲突一定会发生,提前加锁(如
SELECT ... FOR UPDATE),阻塞其他会话的操作。 - 适用场景:并发冲突频繁(如秒杀、库存扣减)。
- InnoDB支持:默认使用悲观锁(如
INSERT/UPDATE/DELETE自动加X锁)。
2. 乐观锁(Optimistic Lock)
- 定义:假设并发冲突不会发生,不提前加锁,而是通过版本控制(如
version列)或时间戳判断数据是否被修改,若修改则回滚。 - 语法:
SELECT * FROM t WHERE id=1;(获取版本号);UPDATE t SET ..., version=version+1 WHERE id=1 AND version=old_version;(验证版本号)。 - 适用场景:并发冲突少(如用户信息修改)。
- InnoDB支持:通过**MVCC(多版本并发控制)**实现(普通
SELECT语句用MVCC,无需加锁)。
四、其他重要概念
1. 锁兼容性矩阵(核心参考)
| 请求锁\持有锁 | S锁(表/行) | X锁(表/行) | IS锁 | IX锁 |
|---|---|---|---|---|
| S锁(表/行) | 兼容 | 冲突 | 兼容 | 兼容 |
| X锁(表/行) | 冲突 | 冲突 | 冲突 | 冲突 |
| IS锁 | 兼容 | 冲突 | 兼容 | 兼容 |
| IX锁 | 兼容 | 冲突 | 兼容 | 兼容 |
2. 锁升级(Lock Escalation)
- 定义:当行级锁的数量超过阈值(如
innodb_max_row_lock_count),InnoDB会将行级锁升级为表级锁,减少锁结构的内存占用。 - 影响:升级后并发性能下降(建议优化索引,减少行锁数量)。
3. 死锁(Deadlock)
- 定义:两个或多个会话互相等待对方的锁,导致无法继续(如会话A持有id=1的X锁,要获取id=2的X锁;会话B持有id=2的X锁,要获取id=1的X锁)。
- InnoDB处理:自动检测死锁(通过
innodb_deadlock_detect参数控制),回滚事务较小的会话(减少损失)。 - 避免方式:统一锁顺序(如先锁id=1,再锁id=2)、减少事务范围、使用
SELECT ... FOR UPDATE明确加锁。
五、不同存储引擎的锁支持
| 存储引擎 | 全局锁 | 表级锁 | 行级锁 | 页级锁 | 意向锁 | MDL锁 | 自增锁 |
|---|---|---|---|---|---|---|---|
| InnoDB | 支持 | 支持 | 支持 | 不支持 | 支持 | 支持 | 支持 |
| MyISAM | 支持 | 支持 | 不支持 | 不支持 | 不支持 | 支持 | 支持 |
| BDB | 支持 | 支持 | 不支持 | 支持 | 不支持 | 支持 | 支持 |
总结
MySQL的锁机制是并发控制的核心,不同粒度的锁适用于不同场景:
- 全局锁:用于全库备份(尽量用InnoDB的
--single-transaction替代)。 - 表级锁:适用于MyISAM存储引擎(并发性能低),或需要锁定全表的操作(如
ALTER TABLE)。 - 行级锁:InnoDB的核心优势(并发性能高),适用于高并发场景(如电商订单、用户信息修改)。
- 意向锁:协调表级锁与行级锁的关键,避免全表扫描。
- MDL锁:保护表结构,防止并发修改冲突。
理解锁的类型、兼容性和使用场景,是优化MySQL并发性能、避免死锁的关键。
