事务隔离级别的实现原理
·
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),这就是间隙锁。这种锁可以防止其他事务在该间隙中插入新数据,从而彻底杜绝了幻读的发生。