From 8a60f69a450f665a0e9f7bab47787b94b8c38041 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Fri, 23 Oct 2026 04:39:20 +0800 Subject: [PATCH] =?UTF-8?q?Update=20=E6=95=B0=E6=8D=AE=E5=BA=93=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E5=8E=9F=E7=90=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- notes/数据库系统原理.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/notes/数据库系统原理.md b/notes/数据库系统原理.md index 6d921ff1..8f6b28b9 100644 --- a/notes/数据库系统原理.md +++ b/notes/数据库系统原理.md @@ -221,7 +221,7 @@ lock-x(A)...unlock(A)...lock-s(B)...unlock(B)...lock-s(C)...unlock(C) MySQL 的 InnoDB 存储引擎采用两段锁协议,会根据隔离级别在需要的时候自动加锁,并且所有的锁都是在同一时刻被释放,这被称为隐式锁定。 -InnoDB 也可以使用特定的语句进行显示锁定: +InnoDB 也可以使用特定的语句进行显式锁定: ```sql SELECT ... LOCK In SHARE MODE; @@ -262,7 +262,7 @@ SELECT ... FOR UPDATE; 在 MVCC 中事务的修改操作(DELETE、INSERT、UPDATE)会为数据行新增一个版本快照。 -脏读和不可重复读最根本的原因是事务读取到其它事务未提交的修改。在事务进行读取操作时,为了解决脏读和不可重复读问题,MVCC 规定只能读取已经提交的快照。当然一个事务可以读取自身未提交的快照,这不算是脏读。 +脏读的原因是事务读取到其它事务未提交的修改,MVCC 规定只能读取已经提交的快照来解决脏读。不可重复读的原因是两次读取之间其它事务提交了修改,可重复读隔离级别下事务始终读取同一个快照来解决这个问题。当然一个事务可以读取自身未提交的快照,这不算是脏读。 ### 版本号 @@ -289,18 +289,17 @@ INSERT、UPDATE、DELETE 操作会创建一个日志,并将事务版本号 TRX ### ReadView -MVCC 维护了一个 ReadView 结构,主要包含了当前系统未提交的事务列表 TRX_IDs {TRX_ID_1, TRX_ID_2, ...},还有该列表的最小值 TRX_ID_MIN 和 TRX_ID_MAX。 +MVCC 维护了一个 ReadView 结构,主要包含了创建 ReadView 时系统中未提交的事务列表 TRX_IDs {TRX_ID_1, TRX_ID_2, ...},该列表的最小值 TRX_ID_MIN,以及下一个将要分配的事务 ID TRX_ID_MAX。

在进行 SELECT 操作时,根据数据行快照的 TRX_ID 与 TRX_ID_MIN 和 TRX_ID_MAX 之间的关系,从而判断数据行快照是否可以使用: -- TRX_ID \< TRX_ID_MIN,表示该数据行快照时在当前所有未提交事务之前进行更改的,因此可以使用。 +- TRX_ID \< TRX_ID_MIN,表示该数据行快照是在当前所有未提交事务之前进行更改的,因此可以使用。 +- TRX_ID \>= TRX_ID_MAX,表示该数据行快照是在 ReadView 创建之后被更改的,因此不可使用。 +- TRX_ID_MIN \<= TRX_ID \< TRX_ID_MAX,如果 TRX_ID 在 TRX_IDs 列表中,表示该数据行快照对应的事务还未提交,则该快照不可使用。否则表示已经提交,可以使用。 -- TRX_ID \> TRX_ID_MAX,表示该数据行快照是在事务启动之后被更改的,因此不可使用。 -- TRX_ID_MIN \<= TRX_ID \<= TRX_ID_MAX,需要根据隔离级别再进行判断: - - 提交读:如果 TRX_ID 在 TRX_IDs 列表中,表示该数据行快照对应的事务还未提交,则该快照不可使用。否则表示已经提交,可以使用。 - - 可重复读:都不可以使用。因为如果可以使用的话,那么其它事务也可以读到这个数据行快照并进行修改,那么当前事务再去读这个数据行得到的值就会发生改变,也就是出现了不可重复读问题。 +提交读和可重复读的判断规则相同,区别在于 ReadView 的创建时机:提交读在每次 SELECT 时都创建新的 ReadView,因此能读到其它事务新提交的修改;可重复读只在事务第一次 SELECT 时创建 ReadView,之后一直复用,因此多次读取的结果相同。 在数据行快照不可使用的情况下,需要沿着 Undo Log 的回滚指针 ROLL_PTR 找到下一个快照,再进行上面的判断。 @@ -335,7 +334,7 @@ SELECT * FROM table WHERE ? for update; Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现。 -MVCC 不能解决幻影读问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读(REPEATABLE READ)隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题。 +在可重复读(REPEATABLE READ)隔离级别下,MVCC 使普通的 SELECT(快照读)不会读到其它事务新插入的行,但是 UPDATE、DELETE、SELECT ... FOR UPDATE 等当前读仍然可能出现幻读,Next-Key Locks 就是为了解决这个问题而存在的。InnoDB 使用 MVCC + Next-Key Locks 在可重复读隔离级别下解决了幻读问题。 ### Record Locks