MVCC原理以及解决脏读、不可重复读、幻读问题


MVCC是什么?有什么作用?

MVCC即多版本并发控制,每行数据存在多个事务版本,通过对数据多个版本的访问可以使读操作不会阻塞写操作,写操作不会阻塞读操作。我们所使用的mysql,其默认引擎为inndb,就支持MVCC,使用MVCC来提高mysql读写的性能。

MVCC的实现原理

MVCC通过行隐藏的字段、undo log日志以及Read View 来实现。

行隐藏的字段

每一行数据含有三个字段,分别是row_id、trx_id和roll_pointer。 row_id:行ID,当存在主键和非空唯一键时默认为row_id,如果不存在,则每一行数据隐藏了行ID trx_id:事务id,即数据对应的事务版本号。 roll_pointer:回滚指针,指向undo log日志中的数据

undo log日志

mysql我们直到其存在三大日志,分别时binlog、undo log、redo log。 undo log日志会在事务执行之前记录数据的信息,当事务执行发生错误需要进行回滚时,会通过undo log日志将数据回滚到执行之前的数据。

版本链

当多个事务对数据进行修改时,会通过行隐藏的回滚指针和undo log日志形成一条版本链。版本链从最新数据的回滚指针指向undo log日志中的历史数据版本,然后相连形成一条版本链。

Read View

我们上面介绍了行隐藏的字段和undo log日志,多个事务可以从版本链中读取想要的数据版本信息,那么每个事务是怎么找到它们想要的版本呢?或者换一种说法它们怎么找到当前事务可见的数据版本呢? 通过Read View实现,当事务执行时进行一次快照读也即普通查询时,会生成一个Read View视图,在版本链中查找数据后,会通过Read View内部的算法来判断是否对当前事务可见,如果不可见,会在版本链中继续遍历。 Read View实现属性 :当生成Read View时,会包含以下属性。 m_ids:当前系统活跃(未提交)的事务id集合 min_limit_id:m_ids中最小的事务id,即最小未提交的事务id max_limit_id:将要生成的事务id,由于事务id递增生成,因此将要生成的事务id对当前事务是不可见的 creator_trx_id:当前事务id Read View判断可见性算法: 对于一个事务trx_id,由Read View内部算法判断是否可见,如下: 1、如果trx_id < min_limit_id,由于事务id是递增的,而事务id小于最小未提交的事务,说明该事务已经提交,对当前事务是可见的。 2、如果trx_id >= max_limit_id,说明该事务版本是在当前事务生成Read View视图之后执行的,对当前事务是不可见的。 3、如果min_limit_id <= trx_id < max_limit_id,进行以下的判断: @1:如果m_ids中包含trx_id,并且trx_id !=creator_trx_id,说明该事务是当前事务生成Read View视图时未提交的事务,并且不是当前事务,对当前事务是不可见的。 @2:如果m_ids中包含trx_id,并且trx_id ==creator_trx_id,说明该事务是当前事务生成Read View视图时未提交的事务,但是该事务是当前事务,因此是可见的。 @3:如果m_ids中不包含trx_id,说明该事务已经提交,对当前事务是可见的。

MVCC在RC下避免脏读

当事务101开启后,修改了id=1的数据后会生成一条事务id=101的数据版本 当事务102开始执行时,进行一次快照读,会生成Read View视图,事务101此时还未提交,生成的视图如下: 此时在版本链中先找到了事务id=101的数据,由于m_ids包含101并且101 != 102,所以事务id为101的数据对当前事务不可见。 再次遍历找到事务id=100的数据,由于100 < 101,所以事务id=100的数据对当前事务是可见的,因此避免了脏读的问题

MVCC在RC造成不可重复读、丢失修改

对于上图执行的顺序,由于在RC每进行一次快照读都会生成一个Read View。 第一次查询数据时: 生成视图: 由于100 < 101,对当前事务可见,读取了事务id=100的数据。 当进行第二次查询时: 生成视图: 由于事务102修改数据,生成事务id=102的数据版本。 当遍历事务id=102的数据版本时,由于 101 <= 102 <103并且m_ids中不包含102,因此事务id=102的数据版本对当亲事务可见,读取了事务id=102的数据。 由于事务的多次查询结果不一样,导致了不可重复读的问题。

MVCC在RR下解决不可重复读问题

在RR隔离级别下,每个事务只能生成一个Read View,就可以保证一个事务中进行多次查询时使用的是同一个视图,就保证了查询的数据相同,解决了不可重复读的问题, 同MVCC解决不可重复读问题一样,MVCC解决了幻读的问题,但是并没有完全解决,还存在一些问题。

RR下仍然存在幻读的问题

如上图,事务102插入了id=10的数据,由于Read View事务101本来是对事务102插入的数据是不可见的,但是由于事务101对插入的数据进行了修改,而当前修改的数据对当前事务是可见的,就导致了事务101读取了新插入的数据,造成了脏读的问题,但是在业务中发生这种情况的概率相当低

相关推荐
CodeClimb3 分钟前
【华为OD-E卷-AI处理器组合100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
码农W10 分钟前
QT--多线程
java·开发语言·qt
1988我想和这个世界谈谈1 小时前
java实现预览服务器文件,不进行下载,并增加水印效果
java·文件预览·水印
PigeonGuan1 小时前
[Linux] 服务器CPU信息
java·linux·服务器
ppo_wu1 小时前
解决:com.mongodb.MongoSocketOpenException: Exception opening socket
java·数据库·spring boot·mongodb
利刃大大1 小时前
【基础篇】三、MySQL表结构的操作
数据库·mysql·oracle
放逐者-保持本心,方可放逐1 小时前
基于Node.js + Koa2 + MySQL + TypeScript的应用示例
mysql·typescript·node.js·koa2·服务端基础应用
幻想多巴胺1 小时前
MySQL 中的触发器:优点和缺点
数据库·mysql
啊烨疯狂学java1 小时前
1231java面经md
java·算法·面试·排序算法
NHuan^_^1 小时前
RabbitMQ基础篇之Java客户端快速入门
java·rabbitmq·java-rabbitmq