MySQL锁
MySQL 的锁机制是保证数据一致性和并发控制的核心组件。不同的锁类型和策略在不同场景下协调事务间的资源竞争。以下从锁的分类、不同锁之间的关系、以及实际应用场景展开详细介绍:
一、MySQL 锁的分类
1. 全局锁(Global Lock)
- 作用:锁定整个数据库实例,确保备份一致性。
- 使用场景:执行
FLUSH TABLES WITH READ LOCK(FTWRL)后,所有表变为只读状态,常用于物理备份工具(如mysqldump)。 - 注意:全局锁对业务影响极大,通常建议使用 InnoDB 引擎的
--single-transaction参数实现无锁一致性读。
2. 表级锁(Table-Level Lock)
表锁(Table Lock):
- 共享锁(S Lock):允许读,阻止其他事务的写操作。
- 排他锁(X Lock):禁止其他事务的读/写。
- 手动触发:
LOCK TABLES ... READ/WRITE,需显式释放(UNLOCK TABLES)。 - 特点:锁粒度粗,并发性低,MyISAM 默认使用表锁。
元数据锁(Metadata Lock, MDL):
- 作用:防止表结构变更(DDL)与数据操作(DML)冲突。
- 自动加锁:DML 操作加 MDL 读锁,DDL 操作加 MDL 写锁。
- 问题:长事务可能阻塞 DDL 操作(如修改字段),导致后续查询被阻塞。
意向锁(Intention Lock)
- 作用:快速判断表是否被行锁占用,避免逐行检查。
- 意向共享锁(IS):事务打算在行上加共享锁。
- 意向排他锁(IX):事务打算在行上加排他锁。
- 兼容性:IS 与 IX 相互兼容,但 IS/IX 与表级 S/X 锁互斥。
3. 行级锁(Row-Level Lock)
仅 InnoDB 支持,锁粒度细,并发性高,但开销较大:
- 记录锁(Record Lock):锁定索引中的某一行(若表无索引,退化为表锁)。
- 间隙锁(Gap Lock):锁定索引记录的间隙(如
id BETWEEN 10 AND 20),防止幻读。 - 临键锁(Next-Key Lock):记录锁 + 间隙锁,锁定左开右闭区间(如
(5, 10])。 - 插入意向锁(Insert Intention Lock):一种特殊的间隙锁,标记准备插入的位置,允许不同插入点并发插入。
二、不同锁之间的关系
1. 层级关系
- 全局锁 > 表级锁 > 行级锁:高层锁会限制底层锁的获取。例如,全局锁会阻塞所有表的写操作。
意向锁与表锁的互斥:
- 事务 A 对某行加 X 锁(自动加 IX 锁),事务 B 想加表级 X 锁时,需检查 IX 锁是否存在,存在则阻塞。
2. 兼容性矩阵
| 当前锁 \ 请求锁 | IS | IX | S | X |
|---|---|---|---|---|
| IS | ✅ | ✅ | ✅ | ❌ |
| IX | ✅ | ✅ | ❌ | ❌ |
| S | ✅ | ❌ | ✅ | ❌ |
| X | ❌ | ❌ | ❌ | ❌ |
规则:
- 共享锁(S)之间兼容,排他锁(X)与其他所有锁互斥。
- 意向锁(IS/IX)之间兼容,但 IS/IX 与表级 S/X 锁可能互斥。
3. 隔离级别的影响
- 读未提交(Read Uncommitted):不加行锁,存在脏读。
- 读已提交(Read Committed):行锁仅锁定实际修改的行,无间隙锁。
- 可重复读(Repeatable Read):默认使用临键锁,通过间隙锁防止幻读。
- 串行化(Serializable):所有 SELECT 自动加共享锁,退化为类似表锁。
三、锁的冲突与死锁
1. 死锁产生条件
- 互斥:资源被独占。
- 占有且等待:事务持有资源并等待其他资源。
- 不可抢占:资源只能由持有者释放。
- 循环等待:事务间形成环形等待链。
2. 死锁处理
- InnoDB 自动检测:通过等待图(Wait-for Graph)检测死锁,回滚代价较小的事务。
- 手动处理:设置
innodb_lock_wait_timeout定义锁等待超时时间(默认 50 秒)。
四、锁的优化建议
- 缩短事务:及时提交事务,避免长事务占用锁资源。
- 访问顺序:按固定顺序访问表/行,减少死锁概率。
- 索引优化:合理设计索引,避免全表扫描(行锁可能退化为表锁)。
- 隔离级别:根据业务需求选择最低隔离级别(如读已提交可减少间隙锁使用)。
- 监控工具:使用
SHOW ENGINE INNODB STATUS或information_schema.INNODB_TRX分析锁状态。
五、示例场景
场景 1:间隙锁防止幻读
-- 事务 A(隔离级别:可重复读)
BEGIN;
SELECT * FROM users WHERE age > 20 FOR UPDATE; -- 加临键锁,锁定 (20, +∞)
-- 事务 B
INSERT INTO users (age) VALUES (25); -- 被阻塞,直到事务 A 提交场景 2:意向锁与表锁互斥
-- 事务 A
BEGIN;
UPDATE users SET name='Bob' WHERE id=1; -- 行级 X 锁 + 表级 IX 锁
-- 事务 B
LOCK TABLES users WRITE; -- 请求表级 X 锁,检测到 IX 锁,阻塞总结
MySQL 的锁机制通过多粒度锁(全局、表、行)和意向锁的配合,平衡了数据一致性与并发性能。理解不同锁的兼容性、层级关系和隔离级别的影响,是设计高并发应用和排查锁问题的关键。实际开发中应结合业务需求,合理选择事务粒度、索引策略和隔离级别,避免锁竞争导致的性能瓶颈。