Skip to content

事务与锁:综览 ​

事务的隔离性不是靠单一机制实现的,而是「MVCC 管读、锁管写」两套机制分工配合。理解事务与锁,先建立这条主线,再看读和写各自走哪条路径。

1. 一条主线:隔离性 = MVCC + 锁 ​

ACID 四项承诺里,隔离性(Isolation)要回答:两个事务并发读写同一份数据时,彼此能看到对方的哪些中间状态。InnoDB 用两套机制共同回答:

机制管什么怎么管载体
MVCC读(快照读)每行保留多个版本,读时按 Read View 挑一个可见版本,不加锁Undo Log 版本链
锁写,以及「要读最新值」的读在索引记录上加行锁,阻塞冲突操作Record / Gap / Next-Key Lock

为什么必须两套配合,单靠锁不够?锁只能挡住「正在改」,挡不住「改完提交了」。要挡住后者(可重复读),必须让读操作读到「事务开始那一刻的快照」而非「数据库最新状态」——这是 MVCC 的职责。反过来,写操作必须基于最新状态:两个事务若都基于同一份旧快照做 balance = balance - 100,会互相覆盖——这是锁的职责。

2. 一条 SQL 的路径分叉 ​

每一句 SQL 进来,先分「读」还是「写」,读再分两条:

  • 快照读:普通 SELECT,走 MVCC,读历史可见版本,不加锁、不被写阻塞。
  • 当前读:SELECT ... FOR UPDATE / SELECT ... LOCK IN SHARE MODE,绕过 MVCC,读最新版本并加锁。
  • 写:INSERT / UPDATE / DELETE,本质先做一次当前读,再加锁。

3. 一笔并发读写里,三件事如何配合 ​

用 事务与 MVCC 开头的场景串一遍:

  1. 事务 B UPDATE(写):先做当前读拿到最新行、加行锁;同时把旧值写进 Undo Log,版本链多一节。
  2. 事务 A 普通 SELECT(快照读):走 MVCC,用 Read View 沿版本链判定可见版本,读到旧值 500,不加锁、不被 B 阻塞。
  3. 事务 B COMMIT:释放行锁;它留下的 Undo 旧版本,等所有 Read View 都不再需要后由 Purge 清理。

这三步把三件事的关系讲清:Undo Log 提供历史版本(MVCC 的数据基础),Read View 判定该读哪个版本,锁保护写与写不冲突。

Released under the MIT License.