人人都会AI编程

6.3 MySQL 锁体系分类

更新时间:2026-07-10

在 InnoDB 的锁体系中,共享锁(S 锁)、排他锁(X 锁)和意向锁(IS/IX 锁)是最基础的三种锁类型。它们就像交通信号灯,规定了不同事务在同时访问同一资源时的通行规则。理解它们的作用和相容性,是分析并发问题和避免死锁的第一步。

共享锁与排他锁

这是最常见的两种行级锁,直接作用于数据行本身。

  • 共享锁(Shared Lock,简称 S 锁):允许持有锁的事务读取一行数据。如果事务 T1 对某行加了 S 锁,其他事务也可以继续对该行加 S 锁(即多个事务可以同时读),但任何事务都不能对该行加 X 锁(即不能修改)。这保证了“读不阻塞读,但阻塞写”。
  • 排他锁(Exclusive Lock,简称 X 锁):允许持有锁的事务更新或删除一行数据。一旦某行被加了 X 锁,其他事务既不能加 S 锁,也不能加 X 锁,直到该锁被释放。这保证了“写阻塞所有读写”。

两者的兼容关系可以用下表表示:

| | S 锁 | X 锁 |
|---|---|---|
| S 锁 | ✅ 兼容 | ❌ 冲突 |
| X 锁 | ❌ 冲突 | ❌ 冲突 |

实际加锁方式:

  • S 锁:普通的 SELECT 语句在 可重复读读已提交 隔离级别下默认是不加锁的,它们通过 MVCC 读取历史快照,因此与写操作完全不冲突。如果需要显式加 S 锁,可以使用 SELECT ... LOCK IN SHARE MODE(MySQL 8.0 后更推荐 SELECT ... FOR SHARE)。典型场景是在读取一行数据后,随后要基于该数据做判断再更新,为了防止读取后数据被其他事务修改,可以先加 S 锁。
  • X 锁:增、删、改操作(INSERTUPDATEDELETE)会自动对相关行加 X 锁。SELECT ... FOR UPDATE 也会对扫描到的行加 X 锁,常用于先读后改场景(比如扣库存前先锁定该行)。在串行化(SERIALIZABLE)隔离级别下,普通的 SELECT 也会被隐式转换为 SELECT ... LOCK IN SHARE MODE,但生产环境极少使用此级别。

注意:锁是加在索引记录上的。如果查询没有走索引,InnoDB 可能会退化为锁住所有扫描到的行,甚至通过临键锁锁住间隙,造成大范围阻塞,这是开发中很容易踩的坑。

意向锁

意向锁是 InnoDB 中的表级锁,它的设计初衷是解决“如何快速判断一张表里是否有行被加锁”的问题。

考虑这样一个场景:事务 T1 已经对 user 表的某一行加了 X 锁,现在事务 T2 想对整张 user 表加 X 锁(比如执行 ALTER TABLELOCK TABLES ... WRITE)。如果没有任何意向锁,T2 就必须一行一行地检查表中是否已经有行被加了 X 锁,这在大表上极不现实。而意向锁的作用就是在表级别打一个标记,告知后来者“表内有行被锁定”。

意向锁分为两种:

  • 意向共享锁(Intention Shared Lock,简称 IS 锁):当事务准备给某些行加 S 锁时,必须先获取该表的 IS 锁。
  • 意向排他锁(Intention Exclusive Lock,简称 IX 锁):当事务准备给某些行加 X 锁时,必须先获取该表的 IX 锁。

意向锁的加锁是 MySQL 自动完成的,开发者无法手动干预。例如,执行一条 SELECT ... FOR SHARE 时,InnoDB 会先在这张表上获取 IS 锁,然后再对特定行加 S 锁。执行一条 DELETE 时,会先在表上获取 IX 锁,再对行加 X 锁。

意向锁的兼容关系:

意向锁之间的兼容规则很直接:IS 和 IX 锁相互之间总是兼容的。因为多个事务完全可以在同一张表里分别给不同行加 S 锁或 X 锁,互不干扰。

意向锁与表级锁(主要是 LOCK TABLES 或 DDL 操作施加的表级 S/X 锁)之间的关系也很明确:

  • 意向锁不会与行级锁冲突,它只是表级的标记。
  • 意向锁与表级锁(LOCK TABLES ... WRITE 等)冲突。例如,当一张表上已经有事务持有 IX 锁(意味着表内某行有 X 锁),另一个事务想对整表加 X 锁(即 DDL 这类操作)时,就会因为 IX 锁的存在而等待。

下表总结了它们与表级 S/X 锁的兼容性:

| | 表级 S 锁 | 表级 X 锁 |
|---|---|---|
| IS 锁 | ✅ 兼容 | ❌ 冲突 |
| IX 锁 | ❌ 冲突 | ❌ 冲突 |

  • IS 与表级 S 锁兼容,因为多个事务都可以同时对整个表读。
  • IS 与表级 X 锁冲突,因为有人想要独占写表,不能允许表中还有行级 S 锁存在。
  • IX 与表级 S 锁冲突,因为表中可能已有行级 X 锁,此时对整表加 S 锁会产生逻辑上的不一致。
  • IX 与表级 X 锁冲突,因为表中已有行级 X 锁,不可能再让其他事务锁住整张表写。

实际开发中,多数 DDL 操作(如 ALTER TABLE 添加字段)会申请表级 X 锁,但如果表上有未结束的长事务(持有 IX 锁),DDL 就会一直等待,导致后续查询全部阻塞,这是线上 DDL 故障的常见根源。MySQL 8.0 支持原子 DDL 和 ALGORITHM=INPLACE 等优化,但意向锁的设计逻辑并没有改变。

一句话总结:共享锁和排他锁控制对数据行的并发访问,意向锁则是在表级别提供轻量级的“预告”,让后续的表级操作能快速判断是否可能冲突。这三者组成的层次化锁体系,既保证了并发度,又避免了逐行检查的巨大开销。