giftiaのblog

事务隔离级别的实现原理

· giftia

MySQL 的 InnoDB 存储引擎主要通过两种核心机制来实现事务隔离:MVCC(多版本并发控制) 和 锁(Locking)。这两者相互配合,在隔离性与性能之间取得了完美平衡。

  • READ UNCOMMITTED:不使用 MVCC,也不使用锁。
  • READ COMMITTED:使用 MVCC,但每次读都生成新视图。
  • REPEATABLE READ:使用 MVCC,并且固定了读视图。同时,在当前读操作中,还会使用 Next-Key Lock 来彻底杜绝幻读。
  • SERIALIZABLE:不使用 MVCC,完全依赖锁机制,将所有操作串行化。

1. MVCC

MVCC 是 InnoDB 实现 READ COMMITTED 和 REPEATABLE READ 的核心。它的基本思想是:不通过加锁来阻塞读操作,而是通过保存数据行的多个版本,让读和写可以并发进行。

实现原理:

  • 隐藏列:InnoDB 的每行数据都包含几个隐藏列,其中最重要的是 DB_TRX_ID(记录最近一次修改该行的事务ID)和 DB_ROLL_PTR(回滚指针,指向上一版本的记录)。这些指针将不同版本的数据串成一个版本链。
  • Read View (读视图):当一个事务执行快照读(即普通的 SELECT 语句)时,InnoDB 会为它创建一个 Read View。这个视图记录了当前活跃(未提交)的事务 ID 列表。
  • 可见性判断:事务在读取数据时,会根据这个 Read View,沿着版本链从新到旧地遍历,直到找到一个对当前事务可见的版本。

MVCC 在不同隔离级别下的应用:

  • READ COMMITTED:每次执行 SELECT 语句时,都会重新生成一个 Read View。因此,它能够读取到最新已提交的数据,但会导致不可重复读。
  • REPEATABLE READ:只在事务第一次执行 SELECT 时生成一个 Read View,并将其固定下来。后续的 SELECT 都使用这个固定的视图。这是它解决不可重复读的关键所在。

2. 锁

锁机制主要用于处理并发事务对同一数据的写操作,以保证数据的一致性。

  • 共享锁(S锁):允许多个事务同时持有,但会阻止其他事务获得排他锁。
  • 排他锁(X锁):一次只能被一个事务持有,会阻塞其他事务的读写操作。

锁机制在不同隔离级别下的应用:

  • READ UNCOMMITTED:完全不加锁,因此会出现脏读。
  • SERIALIZABLE:这是最严格的隔离级别。它对所有读操作都加上共享锁,对所有写操作都加上排他锁,强制事务串行执行,彻底避免了所有并发问题,但性能极低。

3. 彻底解决幻读

间隙锁 (Gap Lock) 和 Next-Key Lock

在 REPEATABLE READ 隔离级别下:

  • 快照读 (SELECT):通过 MVCC 机制,不会读取到新插入的数据,所以避免了幻读。
  • 当前读 (SELECT … FOR UPDATE, UPDATE, DELETE):这类操作需要锁定最新的数据行。InnoDB 在锁定数据行的同时,还会锁定数据行之间的间隙(Gap),这就是间隙锁。这种锁可以防止其他事务在该间隙中插入新数据,从而彻底杜绝了幻读的发生。