2026年7月11日 · 5 分钟阅读

MySQL 锁机制:全局锁、表锁、行锁、间隙锁、临键锁与死锁分析

梳理 MySQL 锁体系,包括读写锁、全局锁、表级锁、MDL、自增锁、InnoDB 行锁、记录锁、间隙锁、Next-Key Lock、插入意向锁、意向锁和死锁规避。

MySQL 的锁机制,是事务隔离和并发控制的基础。很多线上问题,比如更新阻塞、DDL 卡住、死锁、接口超时,背后都和锁有关。

理解 MySQL 锁,要先区分两个维度:锁的模式和锁的粒度。

读锁和写锁

最基础的锁模式是共享锁和排他锁。

也叫特点
共享锁读锁 S Lock多个事务可以同时读
排他锁写锁 X Lock会阻塞其他读锁和写锁

读锁之间不互斥,写锁和读锁、写锁和写锁之间互斥。

按粒度分类

MySQL 锁按粒度可以分为:

锁粒度说明实现层
全局锁锁整个数据库实例Server 层
表级锁锁一张表Server 层
行级锁锁行记录或索引范围存储引擎层,主要是 InnoDB

锁粒度越大,开销越小,但并发能力越差;锁粒度越小,并发能力越好,但实现更复杂。

全局锁

全局锁会让整个数据库实例进入只读状态。常见命令:

flush tables with read lock;

加全局锁后,DML、DDL、事务提交等写操作都会被阻塞。

全局锁典型用途是全库逻辑备份。但现代 MySQL 更常用支持一致性视图的备份方式,尽量避免长时间全局锁影响业务。

表级锁

表级锁锁住整张表。

MySQL 表级锁包括:

  • 表读锁。
  • 表写锁。
  • 元数据锁 MDL。
  • 自增锁 AUTO-INC Lock。

表读锁会阻塞写,表写锁会阻塞读和写。

MDL 元数据锁

MDL 是 Metadata Lock,用于保护表结构元数据。

当执行查询、更新时,会持有 MDL 读锁;执行 DDL 时,需要 MDL 写锁。

典型问题是:一个长事务查询了某张表但一直不提交,后续 DDL 等待 MDL 写锁,再后续普通查询也可能排队等待,导致业务大量阻塞。

所以线上执行 DDL 前,一定要关注长事务。

自增锁

自增锁用于保证自增列分配的正确性。

插入数据时,InnoDB 需要为自增字段分配递增值。不同插入模式下,自增锁的持有时间和并发能力会不同。

它通常不是业务开发最常直接感知的锁,但在高并发批量插入场景中会影响吞吐。

InnoDB 行锁

InnoDB 行锁是通过索引实现的。准确说,它锁的是索引记录,而不是直接锁物理行。

这点非常关键:如果更新条件没有命中索引,可能扫描大量记录并加锁,甚至造成看起来像锁表的效果。

常见行锁包括:

  • 记录锁。
  • 间隙锁。
  • 临键锁。
  • 插入意向锁。

记录锁

记录锁锁住某条索引记录。

例如:

update user set name = '曹操' where id = 1;

如果 id 是主键,InnoDB 会锁住主键索引上 id = 1 这条记录。

间隙锁

间隙锁锁的是索引记录之间的间隙。

例如索引中存在 1020,那么 (10, 20) 这个范围就是间隙。间隙锁的目的是阻止其他事务在这个范围插入新记录。

间隙锁主要用于解决幻读问题。

临键锁 Next-Key Lock

临键锁是记录锁和间隙锁的组合。

Next-Key Lock = Record Lock + Gap Lock

它既锁住索引记录,也锁住记录前面的间隙。

InnoDB 在 RR 隔离级别下,很多范围查询加锁会使用 Next-Key Lock,防止其他事务插入满足条件的新记录。

插入意向锁

插入意向锁是一种特殊的间隙锁,用于表示事务准备在某个间隙插入记录。

多个事务如果插入同一间隙内不同位置,通常可以并发;如果和已有间隙锁冲突,就需要等待。

插入意向锁不是“意向锁”的同义词,它是行级锁体系里的概念。

意向锁

意向锁是表级锁,用于表示事务接下来要在表中某些行上加锁。

常见有:

  • 意向共享锁 IS。
  • 意向排他锁 IX。

它的作用是协调表锁和行锁。比如一个事务已经对表中某些行加了排他锁,另一个事务想对整张表加写锁时,可以通过意向锁快速判断是否冲突,而不用逐行检查。

RC 和 RR 下加锁差异

Read Committed 和 Repeatable Read 下,InnoDB 加锁行为不同。

一般来说:

  • RC 更少使用间隙锁,并发性更好。
  • RR 为了解决幻读,范围加锁时更容易使用间隙锁或 Next-Key Lock。

这也是为什么同一条 SQL,在不同隔离级别下可能锁住不同范围。

为什么索引会影响锁范围

InnoDB 行锁基于索引。如果查询条件能精确命中唯一索引,锁范围通常较小;如果是范围查询,锁范围会扩大;如果没有合适索引,可能扫描并锁住更多记录。

所以优化锁冲突,很多时候不是先改事务隔离级别,而是先检查 SQL 是否走了正确索引。

死锁是什么

死锁是两个或多个事务互相等待对方持有的锁,导致谁都无法继续。

典型场景:

事务 A 锁住 id=1,等待 id=2
事务 B 锁住 id=2,等待 id=1

MySQL 默认会主动检测死锁,并回滚其中一个影响较小的事务,让另一个事务继续执行。

如何减少死锁

减少死锁的常见方法:

  1. 保持事务短小,尽快提交。
  2. 多个事务按相同顺序访问资源。
  3. 尽量使用主键或唯一索引更新。
  4. 避免大范围扫描加锁。
  5. 避免在事务中执行耗时业务逻辑。
  6. 合理拆分批量更新。
  7. 对死锁异常做好重试。

死锁不一定能完全避免,关键是降低概率并让业务具备重试能力。

小结

MySQL 锁机制可以这样理解:

  1. 读锁共享,写锁排他。
  2. 全局锁影响整个实例,表锁影响整张表,行锁影响索引记录和范围。
  3. MDL 常导致 DDL 被长事务阻塞。
  4. InnoDB 行锁基于索引实现。
  5. 间隙锁和 Next-Key Lock 用于解决幻读。
  6. 死锁的关键是事务访问资源顺序交叉。

掌握锁机制后,排查“为什么 SQL 卡住”就有了方向。