mysql5.7系列-InnoDB的MVCC实现原理

发布于:2025-07-08 ⋅ 阅读:(15) ⋅ 点赞:(0)

谈到数据库事务都要提一下ACID 特性:

原子性(Atomicity):事务中的操作要么全部执行,要么全部不执行。

一致性(Consistency):事务执行前后,数据库的状态必须是一致的。

隔离性(Isolation):一个事务的执行不应受其他事务的干扰,每个事务都应有自己的数据视图。

持久性(Durability):一旦事务提交,其结果就会永久保存。

什么是mvcc

mvcc全称Multi-Version Concurrency Control ,多版本并发控制,顾名思义是维持了数据库中数据的多版本;这个机制主要是为了服务事务隔离级别中的READ COMMITTED和REPATEABLE READ两种隔离级别在多个事务读取数据的时候能遵守sql标准。

本质:就是通过Read View + Undo Log来实现的,Undo Log中保存了历史快照,而Read View 用来判断具体哪一个快照是可见的。

下面具体介绍下,什么是 Undo Log和Read View:

什么是Undo Log

undo log是innodb引擎的一种日志,在事务的修改记录之前,会把该记录的原值(before image)先保存起来(undo log)再做修改,以便修改过程中出错能够恢复原值或者其他的事务读取。

数据库中的每行记录中,除了保存了我们自己定义的一些字段以外,还有一些重要的隐式字段的:

● db_row_id:隐藏主键,如果我们没有给这个表创建主键,那么会以这个字段来创建聚簇索引。

● db_trx_id:对这条记录做了最新一次修改的事务的ID

● db_roll_ptr:回滚指针,指向这条记录的上一个版本,其实他指向的就是Undo Log中的上一个版本的快照的地址。

因为每一次记录变更之前都会先存储一份快照到undo log中,那么这几个隐式字段也会跟着记录一起保存在undo log中,就这样,每一个快照中都有一个db_trx_id字段表示了对这个记录做了最新一次修改的事务的ID ,以及一个db_roll_ptr字段指向了上一个快照的地址。这样就产生了一个版本快照链,如下:

什么是Read View

在 RC或者RR事务隔离级别下,当前事务产生select查询时就会产生read view, RC是每次select都会产生一个read view, RR是只在第一次select的时候产生一个read view 后面的select都是使用这个read view

ReadView 中主要包含4个比较重要的内容:

  • m_ids :表示在生成ReadView 时当前系统中活跃的读写事务的事务id 列表。
  • min_trx_id :表示在生成ReadView 时当前系统中活跃的读写事务中最小的事务id ,也就是m_ids 中的最小值。
  • max_trx_id :表示生成ReadView 时系统中应该分配给下一个事务的id 值。

小贴士: 注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。

  • creator_trx_id :表示生成该ReadView 的事务的事务id 。小贴士: 我们前边说过,只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为0。

而判断数据记录可见性的逻辑就是通过readview和【行记录的隐藏字段trx_id】做对比的

一个事务去访问记录的时候,怎么判断记录的可见性呢?

Read View决定当前事务能读到哪个版本的数据,从表记录到Undo Log历史数据的版本链,依次匹配,满足哪个版本的匹配规则,就能读到哪个版本的数据,一旦匹配成功就不再往下匹配。

遵循了以下可见性匹配规则:

规则说明:

  • trx_id = creator_trx_id:如果 trx_id 值等于创建Read View的事务Id,那么数据记录的最后一次操作的事务就是当前事务,该版本的记录对当前事务可见
  • trx_id < min_trx_id:如果 trx_id 值小于 Read View 中的 min_trx_id ,表示这个版本的记录是在创建 Read View 已经提交的事务生成的,所以该版本的记录对当前事务可见
  • trx_id >= max_trx_id:如果trx_id 值小于 Read View 中的 min_trx_id ,表示这个版本的记录是在创建 Read View 才启动的事务生成的,所以该版本的记录对当前事务不可见
  • min_trx_id <= trx_id < max_trx*id:判断 *trx_id 是不是在当前事务ID集合(m_ids)里面
  • 如果在m_ids中,则代表Read View生成时刻,这个事务还在活跃,还没有Commit,版本记录在前事务不可见
  • 如果不在m_ids中,则说明,这个事务在Read View生成之前就已经Commit了,版本记录在前事务可见

mvcc有什么作用

1.解决脏读问题:一个事务中的每一次SELECT都会重新获取一次Read View。

2.解决不可重复读问题:一个事务中只在第一次SELECT的时候会获取一次Read View。

3.解决部分幻读问题:mvcc+间隙锁可以解决部分幻读问题,但是不能完全解决。

参考资料

https://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html

https://www.cnblogs.com/ykmStudy/p/17765346.html


网站公告

今日签到

点亮在社区的每一天
去签到