MySQL InnoDB 的锁机制
引言
锁是数据库管理并发访问的另一种核心机制,与 MVCC 相辅相成。本文将系统梳理 MySQL InnoDB 中锁的粒度、类型和工作原理,并深入探讨它如何与事务隔离级别配合,共同保障数据的一致性和完整性。
一、 锁的粒度:由粗到细
InnoDB 支持多种粒度的锁,允许系统在锁开销和数据安全性之间进行权衡:
-
全局锁:锁定整个数据库实例,主要用于全库逻辑备份,影响巨大。
-
表级锁:锁定整张表,主要包括:
-
元数据锁 (MDL):由 Server 层实现,保证读写的表结构一致性。
-
意向锁 (Intention Locks):一种表级锁,用于快速判断表内是否有行锁。它表明“一个事务想要在表中的某些行上获取共享锁或排他锁”。
-
意向共享锁 (IS):事务打算给某些行加S锁。
-
意向排他锁 (IX):事务打算给某些行加X锁。
-
-
AUTO-INC 锁:一种特殊的表级锁,用于在插入时生成自增列的值。在 MySQL 8.0 中,轻量级模式已大幅减少其影响。
-
-
行级锁:InnoDB 并发能力的核心,锁定索引记录。
二、 行级锁的三种算法
行锁是 InnoDB 的精髓,它包含三种算法,用于在不同场景下防止幻读和数据冲突:
-
记录锁 (Record Lock):锁定索引中的一条具体记录。
-
间隙锁 (Gap Lock):锁定索引记录之间的间隙(开区间),防止其他事务在这个间隙中插入新记录,从而有效防止幻读。
-
临键锁 (Next-Key Lock):记录锁 + 间隙锁的组合,锁定一个记录及其前面的间隙(左开右闭区间)。它是 InnoDB 在 可重复读 (RR) 隔离级别下默认的行锁算法。
三、 锁的兼容性与死锁
-
共享锁 (S) 与排他锁 (X):基本的行锁类型。
-
S锁(读锁):允许其他事务读,但不能写。
SELECT ... LOCK IN SHARE MODE
会加 S锁。 -
X锁(写锁):不允许其他事务读(当前读)或写。
UPDATE
,DELETE
,INSERT
,SELECT ... FOR UPDATE
会加 X锁。 -
S锁与S锁兼容,S锁与X锁、X锁与X锁互斥。
-
-
死锁:不同的事务以不同的顺序请求和持有锁,导致互相等待。InnoDB 有死锁检测机制,会自动选择回滚其中一个代价较小的事务。
四、 锁与隔离级别的实战
锁的使用策略与隔离级别密不可分:
-
READ COMMITTED (RC):
-
快照读:使用 MVCC,无锁。
-
当前读(
FOR UPDATE
等)及写操作:仅使用记录锁。因为不加间隙锁,所以无法避免幻读。
-
-
REPEATABLE READ (RR):
-
快照读:使用 MVCC,无锁。
-
当前读及写操作:使用临键锁(Next-Key Lock),通过在搜索范围内加锁,彻底杜绝幻读。(特例:唯一索引的唯一查询会退化为记录锁)。
-
-
SERIALIZABLE (可串行化):
-
所有读操作(即使是普通 SELECT)都会自动转换为
SELECT ... LOCK IN SHARE MODE
,即加共享锁。读写严重互斥,并发性能最低。
-
五、 特殊锁:插入意向锁 (Insert Intention Locks)
这是一种特殊的间隙锁,不是表级意向锁。它在执行 INSERT
操作前设置,表示想往某个间隙里插入一条记录。它的存在是为了提高并发插入的性能:
-
多个事务可以在同一个间隙上同时持有插入意向锁(只要插入的位置不同)。
-
但它会与独占的间隙锁/临键锁冲突。这正是防止幻读的关键:一个事务持有间隙锁,另一个事务的插入意向锁就会被阻塞。
更多资料:0voice · GitHub