MySQL学习笔记(3)InnoDB存储引擎对MVCC的实现
本文参考https://javaguide.cn/database/mysql/innodb-implementation-of-mvcc.html
什么是MVCC
MVCC(Multi-Version Concurrecy Control) 多版本并发控制
MVCC 是一种并发控制机制,用于在多个并发事务同时读写数据库时保持数据的一致性和隔离性。它是通过在每个数据行上维护多个版本的数据来实现的。当一个事务要对数据库中的数据进行修改时,MVCC 会为该事务创建一个数据快照,而不是直接修改实际的数据行。
读操作(SELECT)
当一个事务进行读取的操作时,并不会读取实际行上的数据,而是在事务开始的时刻为需要读取的数据创建一个快照,防止该事务读取其他事务中提交或修改的数据。
- 对于读取操作,事务会查找符合条件的数据行,并选择符合其事务开始时间的数据版本进行读取。
- 如果某个数据行有多个版本,事务会选择不晚于其开始时间的最新版本,确保事务只读取在它开始之前已经存在的数据。
- 事务读取的是快照数据,因此其他并发事务对数据行的修改不会影响当前事务的读取操作。
写操作(INSERT、UPDATE、DELETE)
当一个事务执行写操作时,它会生成一个新的数据版本,并将修改后的数据写入数据库。
- 对于写操作,事务会为要修改的数据行创建一个新的版本,并将修改后的数据写入新版本。
- 新版本的数据会带有当前事务的版本号,以便其他事务能够正确读取相应版本的数据。
- 原始版本的数据仍然存在,供其他事务使用快照读取,这保证了其他事务不受当前事务的写操作影响。
事务的回滚和提交
- 当一个事务提交时,该数据便成为了最新版数据,对其他事务可见。
- 当一个事务回滚时,他所作的修改被撤销,对其他事务不可见。
一致性锁定读(当前读)和非锁定读
在 InnoDB 存储引擎中,多版本控制 (multi versioning) 就是对非锁定读的实现。如果读取的行正在执行 DELETE 或 UPDATE 操作,这时读取操作不会去等待行上锁的释放。相反地,InnoDB 存储引擎会去读取行的一个快照数据,对于这种读取历史数据的方式,我们叫它快照读 (snapshot read)
InnoDB 对 MVCC 的实现
InnoDB对于MVCC的实现依赖于三个部分
隐藏字段
undo log
Read View
隐藏字段
InnoDB会自动为每行的数据加上几个隐藏的字段
- DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id。此外,delete 操作在内部被视为更新,只不过会在记录头 Record header 中的 deleted_flag 字段将其标记为已删除
- DB_ROLL_PTR(7字节) 回滚指针,指向该行的 undo log(上一版本的历史数据) 。如果该行未被更新,则为空
- DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB 会使用该 id 来生成聚簇索引
undo log
undo log作用:
- 当事务回滚时用于将数据恢复到修改前的样子
- 另一个作用是 MVCC ,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现非锁定读
undo log版本链
不同事务或相同事务对同一条记录进行修改, 会导致该记录的undolog生成一条记录版本链表,链表的头部是最新的旧记录,链表尾部是最早的旧记录。
Read View
ReadView (读视图)是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的) id,包含了四个核心字段:
活跃事务:生成Read View时未提交的事务
class ReadView {
/* ... */
private:
trx_id_t m_low_limit_id; /* 大于等于这个 ID 的事务均不可见 */
trx_id_t m_up_limit_id; /* 小于这个 ID 的事务均可见 */
trx_id_t m_creator_trx_id; /* 创建该 Read View 的事务ID */
trx_id_t m_low_limit_no; /* 事务 Number, 小于该 Number 的 Undo Logs 均可以被 Purge */
ids_t m_ids; /* 创建 Read View 时的活跃事务列表 */
m_closed; /* 标记 Read View 是否 close */
}
可见性算法
trx_id: 数据行中的隐藏字段其一(注意要和creator_trx_id区分)
在READ COMMIT于REPEATABLE READ隔离级别下的区别
- READ COMMITTED :在事务中
每一次
执行快照读时生成ReadView。 - REPEATABLE READ:仅在事务中
第一次
执行快照读时生成ReadView, 后续复用该ReadView。
MVCC + Next-Key Lock 防止幻读
- 执行普通 select,此时会以 MVCC 快照读的方式读取数据在快照读的情况下,RR 隔离级别只会在事务开启后的第一次查询生成 Read View ,并使用至事务提交。所以在生成 Read View 之后其它事务所做的更新、插入记录版本对当前事务并不可见,实现了可重复读和防止快照读下的 “幻读”.
- 执行 select…for update/lock in share mode、insert、update、delete 等当前读在当前读下,读取的都是最新的数据,如果其它事务有插入新的记录,并且刚好在当前事务查询范围内,就会产生幻读!InnoDB 使用 Next-key Lock 来防止这种情况。当执行当前读时,会锁定读取到的记录的同时,锁定它们的间隙,防止其它事务在查询范围内插入数据。只要我不让你插入,就不会发生幻读.