MySQL 的锁机制是保证数据一致性、事务隔离性和并发控制的核心。锁可以从粒度(锁定的范围)和类型(锁定的模式)两个主要维度进行分类:
一、按锁的粒度分类 (Lock Granularity)
-
全局锁 (Global Lock):
- 锁定范围: 整个数据库实例。
- 作用: 使数据库处于只读状态。任何对数据的修改操作(DML: INSERT, UPDATE, DELETE)或表结构变更(DDL)都会被阻塞。
- 实现:
FLUSH TABLES WITH READ LOCK(FTWRL)。主要用于逻辑备份(如mysqldump配合--single-transaction时,对于非事务表或需要保证绝对一致性时)。 - 影响: 非常重,严重影响整个数据库的写入可用性,应谨慎使用。
-
表级锁 (Table-Level Lock):
- 锁定范围: 整张表。
- 类型:
- 表共享读锁 (Table Read Lock / Shared Lock):
- 允许多个会话同时获取该锁并读取表数据。
- 阻止其他会话获取该表的写锁(即阻塞写操作)。
- 语法:
LOCK TABLES table_name READ;
- 表独占写锁 (Table Write Lock / Exclusive Lock):
- 只允许一个会话获取该锁。持有该锁的会话可以读写表数据。
- 阻止其他会话获取该表的任何读锁或写锁(即阻塞所有读写操作)。
- 语法:
LOCK TABLES table_name WRITE;
- 表共享读锁 (Table Read Lock / Shared Lock):
- 特点: 实现简单,开销小;但并发度最低,粒度最粗。一个会话在操作表时(即使是只读),其他会话的写操作(甚至某些读操作)可能被阻塞。
- 存储引擎: MyISAM, MEMORY 等非事务性存储引擎主要使用表级锁。InnoDB 在特定情况下(如执行某些 DDL 语句
ALTER TABLE但未使用ALGORITHM=INPLACE/ALGORITHM=INSTANT,或显式执行LOCK TABLES)也会使用表级锁。 - 元数据锁 (Metadata Lock - MDL):
- 一种特殊的表级锁,由 Server 层管理,与存储引擎无关。
- 作用: 保护表结构(元数据)的完整性。当对一个表进行 CRUD 操作时,会自动加 MDL 读锁;当要修改表结构(DDL)时,需要加 MDL 写锁。
- 影响: DDL 操作(如
ALTER TABLE,DROP TABLE)需要等待该表上所有活动事务(持有 MDL 读锁)提交或回滚后才能获取 MDL 写锁。反之,一个长事务持有 MDL 读锁会阻塞后续的 DDL 操作。这是 DDL 操作阻塞或导致阻塞的常见原因。
-
行级锁 (Row-Level Lock):
- 锁定范围: 表中的单行记录(或索引记录)。
- 特点: 粒度最细,并发度最高;但加锁开销最大,管理最复杂。
- 存储引擎: InnoDB 引擎支持行级锁(也是其实现高并发和事务 ACID 特性的基石)。MyISAM 不支持行级锁。
- 类型 (详见下文按类型分类): 共享锁 (S), 排他锁 (X), 意向共享锁 (IS), 意向排他锁 (IX), 记录锁 (Record Lock), 间隙锁 (Gap Lock), 临键锁 (Next-Key Lock), 插入意向锁 (Insert Intention Lock)。
二、按锁的类型/模式分类 (Lock Mode)
这主要针对 InnoDB 的行级锁(但也包含表级的意向锁):
-
共享锁 (Shared Lock - S Lock):
- 目的: 用于读取操作(
SELECT ... FOR SHARE或旧语法SELECT ... LOCK IN SHARE MODE)。 - 兼容性: 多个事务可以同时持有同一数据行的 S 锁(允许并发读)。
- 冲突: 与任何 X 锁冲突(即一个事务持有 S 锁时,其他事务不能获得该行的 X 锁进行写操作)。
- 目的: 用于读取操作(
-
排他锁 (Exclusive Lock - X Lock):
- 目的: 用于写入操作(
UPDATE,DELETE,INSERT,SELECT ... FOR UPDATE)。 - 兼容性: 一个数据行上的 X 锁是排他的。
- 冲突: 与任何其他锁(S 锁或 X 锁)都冲突(即一个事务持有 X 锁时,其他事务不能获得该行的 S 锁或 X 锁)。
- 目的: 用于写入操作(
-
意向锁 (Intention Lock - 表级锁):
- 目的: 表明一个事务“打算”在表中的某些行上加什么类型的行锁。 它们是表级锁,用来协调表级锁请求与行级锁请求,避免 Server 层为了检查行锁兼容性而遍历每一行。
- 类型:
- 意向共享锁 (Intention Shared Lock - IS): 事务打算在表中的某些行上设置 S 锁。
SELECT ... FOR SHARE会加 IS 锁。 - 意向排他锁 (Intention Exclusive Lock - IX): 事务打算在表中的某些行上设置 X 锁。
SELECT ... FOR UPDATE,UPDATE,DELETE,INSERT会加 IX 锁。
- 意向共享锁 (Intention Shared Lock - IS): 事务打算在表中的某些行上设置 S 锁。
- 兼容性 (表级兼容性):
- IS 与 IS 兼容,IS 与 IX 兼容。
- IX 与 IX 不兼容(因为两个事务都打算写不同的行,虽然实际行锁可能不冲突,但表级的 IX 锁本身不兼容)。
- IS/IX 与表级 S 锁不兼容(
LOCK TABLES ... READ)。 - IS/IX 与表级 X 锁不兼容(
LOCK TABLES ... WRITE)。
- 注意: 意向锁之间不会互相阻塞,它们只阻塞表级的
LOCK TABLES ... READ/WRITE请求。意向锁的主要作用是快速判断表上是否有行锁冲突,避免低效的行遍历检查。
-
行级锁的具体实现类型 (InnoDB):
- 记录锁 (Record Lock):
- 锁定索引中具体的某一条记录。
- 总是锁定索引记录(即使表没有显式索引,InnoDB 也会创建一个隐藏的聚集索引)。
- 阻止其他事务修改、删除这条记录,或者在这条记录上加 X 锁(取决于请求锁的类型)。
- 间隙锁 (Gap Lock):
- 锁定索引记录之间的间隙,或者第一个索引记录之前或最后一个索引记录之后的间隙。
- 目的: 防止其他事务在间隙中插入新记录,从而解决幻读问题(Phantom Read)。
- 特点: 多个事务可以在同一个间隙上加间隙锁(兼容的)。间隙锁只在
REPEATABLE READ隔离级别下生效(在READ COMMITTED下通常不生效)。 - 例如:
SELECT * FROM t WHERE id BETWEEN 10 AND 20 FOR UPDATE;会阻止在 id 值 10 到 20 之间的间隙中插入新记录(假设现有记录 id=15)。
- 临键锁 (Next-Key Lock):
- InnoDB 默认的行锁模式。
- 它是 记录锁 (Record Lock) + 间隙锁 (Gap Lock) 的组合。
- 锁定索引记录本身以及该索引记录之前的间隙。
- 目的: 结合了记录锁和间隙锁的功能,在
REPEATABLE READ级别下提供更强的幻读防护。它不仅锁定扫描到的行,还锁定这些行之前的间隙,防止新行插入到间隙中导致幻读。 - 例如:如果索引包含值 10, 11, 13, 20。
SELECT * FROM t WHERE id > 11 AND id < 20 FOR UPDATE;的临键锁可能会锁定 (11, 13], (13, 20) 区间(具体范围取决于执行计划和数据分布)。
- 插入意向锁 (Insert Intention Lock):
- 一种特殊的间隙锁。
- 目的: 表示一个事务打算在某个间隙中插入新记录。多个事务只要插入的位置(间隙)不冲突(即使在同一间隙内插入不同位置),它们可以同时持有该间隙上的插入意向锁。
- 冲突: 插入意向锁之间不会互相冲突(允许多个事务并发插入到同一个间隙的不同位置),但它会等待该间隙上已经存在的其他类型的锁(如 Gap Lock, Next-Key Lock)释放。这是为了避免插入操作阻塞而设计的信号锁。
- 记录锁 (Record Lock):
-
自增锁 (AUTO-INC Lock - 表级锁):
- 目的: 专为具有
AUTO_INCREMENT列的表设计,用于在插入操作时安全地生成自增值。 - 特点: 这是一种特殊的表级锁。当一个事务向包含
AUTO_INCREMENT列的表插入数据时,它必须获取该表的自增锁。持有锁的事务会生成连续的自增值。这可能导致向同一个表插入数据的多个事务之间出现串行化现象(特别是在INSERT ... SELECT这种批量插入时)。 - 模式: MySQL 5.1+ 引入了配置选项
innodb_autoinc_lock_mode来优化自增锁的行为(0: traditional, 1: consecutive (default), 2: interleaved),以在数据安全性和插入并发度之间取得平衡。
- 目的: 专为具有
总结与关键点
- 粒度选择: 优先使用行级锁(InnoDB)以获得高并发。理解表级锁(尤其是 MDL)的影响。
- 锁类型核心: S 锁(读锁)和 X 锁(写锁)是基础。意向锁(IS/IX)是表级辅助锁。
- InnoDB 行锁实现: 理解 Record Lock, Gap Lock, Next-Key Lock, Insert Intention Lock 的作用和触发条件至关重要,特别是在
REPEATABLE READ隔离级别下解决幻读问题。 - 元数据锁 (MDL): DDL 操作阻塞或被阻塞的常见元凶。
- 自增锁: 影响插入性能,可通过
innodb_autoinc_lock_mode调整。 - 隔离级别: 锁的行为(特别是 Gap Lock 和 Next-Key Lock 的使用)强烈依赖于事务的隔离级别(
READ COMMITTEDvsREPEATABLE READ)。 - 死锁: 细粒度的行锁和复杂的加锁顺序增加了死锁的可能性。InnoDB 有死锁检测和回滚机制。
理解 MySQL 的各种锁及其交互方式是进行高性能数据库设计、优化 SQL 查询、诊断并发问题和解决死锁的基础。务必结合具体的存储引擎(主要是 InnoDB)和事务隔离级别来分析锁的行为。
782

被折叠的 条评论
为什么被折叠?



