锁机制

锁(Lock)机制是并发控制的核心手段,用来在多个事务同时读写数据时保证一致性(Consistency)隔离性(Isolation),避免脏读、不可重复读、幻读、丢失更新等问题。

下面按“做什么 → 锁什么 → 怎么锁 → 常见类型 → 典型问题”来梳理。


一、为什么需要锁

数据库支持多事务并发,如果不加控制会出现:

  • 脏写:T1 改了,T2 又改,T1 回滚导致 T2 的修改消失
  • 脏读:读到别的事务未提交的数据
  • 不可重复读:同一事务内两次读同一行结果不同
  • 幻读:同一查询条件,两次查出来的行数不同

锁的本质:让某些操作互斥或有序执行


二、锁的基本维度

1. 按锁的粒度(Granularity)

  • 行锁(Row Lock):锁单行,并发最高(MySQL InnoDB、Oracle 默认偏向)
  • 页锁(Page Lock):锁一个数据页(SQL Server 早期常见)
  • 表锁(Table Lock):锁整张表,开销小但并发差(MyISAM 常用)
  • 全局锁:锁整个数据库(如 FLUSH TABLES WITH READ LOCK

粒度越细 → 并发越好,但管理开销越大


2. 按锁的兼容性(模式)

共享锁(S 锁 / Read Lock)

  • 允许多个事务同时读
  • 阻塞写锁
SELECT * FROM t WHERE id=1 LOCK IN SHARE MODE; -- MySQL

排他锁(X 锁 / Write Lock)

  • 只允许一个事务持有
  • 阻塞其他读锁和写锁
SELECT * FROM t WHERE id=1 FOR UPDATE;
UPDATE t SET ... WHERE id=1;

意向锁(Intention Locks)

  • 表级“声明”:我准备在表里锁某几行
  • 提高“表锁 vs 行锁”冲突检测效率
  • 如:IS(意向共享)、IX(意向排他)

三、InnoDB(MySQL)中的核心锁机制

1. 行锁实现方式

InnoDB 行锁是基于索引的:

  • 走索引 → 锁索引记录(Record Lock)
  • 没走索引 → 升级为近似表锁(锁很多行 / 全表扫描)

2. 三种行锁类型

  • Record Lock:锁某一条索引记录
  • Gap Lock:锁“记录之间的间隙”,防插入(解决幻读)
  • Next-Key Lock = Record + Gap(默认 RR 级别防幻读)

例:

索引值:10, 20, 30
Next-Key Lock 可能锁住 (-∞,10], (10,20], (20,30], (30,+∞)

四、事务隔离级别与锁的关系

(以 SQL 标准 + InnoDB 为例)

隔离级别 脏读 不可重复读 幻读 锁策略
Read Uncommitted 可能 可能 可能 基本不加读锁
Read Committed (RC) 可能 可能 读不加锁(MVCC),写加 X 锁
Repeatable Read (RR) 否* Next-Key Lock(InnoDB 默认)
Serializable 读也加 S 锁,完全串行

MySQL InnoDB 在 RR 下通过 MVCC + Next-Key Lock 解决幻读


五、MVCC 与锁的关系(重点)

现代数据库(MySQL InnoDB、PostgreSQL)并不是“所有读都加锁”:

  • 普通 SELECT(快照读):走 MVCC,读历史版本,不加锁
  • SELECT … FOR UPDATE / LOCK IN SHARE MODE(当前读):加锁
  • 写操作(UPDATE/DELETE):先当前读,再加 X 锁

所以:
锁主要负责“写-写”和“当前读-写”的互斥,MVCC 解决大多数“读-写”并发


六、常见锁问题

1. 死锁(Deadlock)

两个事务互相等待对方释放锁:

T1: 锁 A → 请求 B
T2: 锁 B → 请求 A

解决方式:

  • 数据库自动检测并回滚代价小的事务
  • 业务层:固定加锁顺序、缩短事务、降低隔离级别

2. 锁等待超时

ERROR 1205: Lock wait timeout exceeded

原因:长事务、热点行更新、索引缺失导致锁范围过大

3. 锁升级

  • SQL Server:行锁 → 页锁 → 表锁(减少锁数量)
  • MySQL InnoDB:不会显式“升级”,但无索引时等于锁全表

七、一句话总结

数据库锁机制 = 用不同粒度/模式的锁 + MVCC,在并发和一致性之间做权衡;
行锁保并发,X/S 锁保互斥,Gap/Next-Key 锁防幻读,死锁靠检测和设计规避。

上一篇
下一篇