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. 兼容性矩阵

当前锁 \ 请求锁ISIXSX
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 秒)。

四、锁的优化建议

  1. 缩短事务:及时提交事务,避免长事务占用锁资源。
  2. 访问顺序:按固定顺序访问表/行,减少死锁概率。
  3. 索引优化:合理设计索引,避免全表扫描(行锁可能退化为表锁)。
  4. 隔离级别:根据业务需求选择最低隔离级别(如读已提交可减少间隙锁使用)。
  5. 监控工具:使用 SHOW ENGINE INNODB STATUSinformation_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 的锁机制通过多粒度锁(全局、表、行)和意向锁的配合,平衡了数据一致性与并发性能。理解不同锁的兼容性、层级关系和隔离级别的影响,是设计高并发应用和排查锁问题的关键。实际开发中应结合业务需求,合理选择事务粒度、索引策略和隔离级别,避免锁竞争导致的性能瓶颈。

最后修改:2025 年 03 月 11 日
如果觉得我的文章对你有用,请随意赞赏