MVCC机制

个人理解篇,不一定对,应付面试的时候看的

MVCC(Multi-Version Concurrency Control)全称多版本并发控制,主要用在隔离模式下的提交读、可重复读模式下,依赖于readview和undolog链

一、readview

1、结构

|----------------|--------------------|
| 字段 | 备注 |
| m_ids | 活跃事务id集合 |
| min_trx_id | 活跃事务id集合中最小事务id |
| max_trx_id | 当前分配给新事务的最大事务id+1 |
| creator_trx_id | 生成readview时,当前事务id |

2、举例

现在系统中开启了事务A、B、C,对应的trx_id分别是1、2、3

此时有个新事务的读操作时,会产生如下readview

|----------------|-------|
| m_ids | 1,2,3 |
| min_trx_id | 1 |
| max_trx_id | 5 |
| creator_trx_id | 4 |

二、undolog链

1、结构

|-------------|---------------------------------|
| 字段 | 备注 |
| trx_id | 事务id |
| roll_point | 上一事务指针,指向上一个undolog节点 |
| key | 操作行记录的主键id |
| type | 执行类型(update、insert、delete) |
| commonvalue | 具体的值,没搞清楚具体是什么,但是新值、旧值、更新逻辑应该都有 |

2、举例

现在系统中开启了事务A、B、C,对应的trx_id分别是1、2、3

此时有个新事务的读操作时,会读取一下undolog链如下

三、操作

1、新事物发起读操作时产生readview,然后从undolog链开始查找并进行如下判断逻辑

if(trx_id == creator_trx_id)访问的是自己的事务,允许访问

if(trx_id < min_trx_id)访问的是已经提交的事务,允许访问

if(trx_id >= max_trx_id)访问的是在生成readview之后的事务,不允许访问

if(min_trx_id <= trx_id < max_trx_id)访问的是活跃中未提交的事务,不允许访问

ps:

1、关于undo链中的举例结构图,其实可以看出,默认在 trx_id=1的条目之前还会有trx_id=0的条目 这个条目是在生成readview时最新的提交数据,在trx_id=3后如果有新事务的话,其实也还有指向trx_id=3的事务,这只是个片段,具体的判断阶段会在这图中这一部分判断的比较集中。

2、关于trx_id=1指向的上一个已提交事务的undolog条目,由mysql来采取一些算法来判断是否存在,一般在被引用的时候都是存在的,不必担心找不到已提交的undolog条目

3、每个undolog条目都是某个表的某一行的数据,如果update修改了10条记录,那么会产生10个undolog条目,同时每个undolog链其实只是某一行的版本变化

四、关于

1、RC和RR区别

mvcc可以在RC(读已提交)和RR(可重复读)中,区别就是RC模式下,每次读都会产生readview,而在可重复读模式下,事务结束之前都是复用事务开始后的第一次读产生的readview

2、RR幻读问题

RR中其实没有完全解决幻读问题,如下场景

2.1表Subject:
场景一:

|-----------------------------------------------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| 事务A | 事务B | 事务B查询结果 |
| start TRANSACTION; | start TRANSACTION; | |
| | select * from Subject where difficultly = "困难"; | |
| insert into `Subject`(difficultly,type,language) value ('困难','英语','C'); commit; | | |
| | select * from Subject where difficultly = "困难" for update; | |
| | select * from Subject where difficultly = "困难" | |

可以看到第四步的当前读会出现幻读,而第五步的快照读仍然正常

场景二:

|-----------------------------------------------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| 事务A | 事务B | 事务B查询结果 |
| start TRANSACTION; | start TRANSACTION; | |
| | select * from Subject where difficultly = "困难" for update; | |
| insert into `Subject`(difficultly,type,language) value ('困难','英语','C'); commit; | | |
| | select * from Subject where difficultly = "困难" for update; | |
| | select * from Subject where difficultly = "困难" | |

第三步中事务A在执行插入时会一直无法执行完成,直到事务B中commit后才会执行完成

场景一和场景二区别:

场景一中事务B在事务A插入新数据和提交后执行的当前读,而场景二中事务B在事务A插入新数据和提交前执行的当前读,mysql通过mvcc和临键锁会锁定当前读,这样事务A再插入就插入不了新数据了,就解决了幻读,而场景一是在使用临键锁之前提交的,当前读读的是最新数据,所以会出现幻读

相关推荐
Wang's Blog31 分钟前
MongoDB小课堂: 文档操作核心技术指南:主键机制、CRUD操作与最佳实践
数据库·mongodb
g***26791 小时前
最新SQL Server 2022保姆级安装教程【附安装包】
数据库·性能优化
风123456789~2 小时前
【OceanBase专栏】OB背景知识
数据库·笔记·oceanbase
4***72132 小时前
【玩转全栈】----Django模板语法、请求与响应
数据库·python·django
c***42102 小时前
Django视图与URLs路由详解
数据库·django·sqlite
数据库学啊2 小时前
团队小希望运维简单,时序数据库选型有什么推荐?
运维·数据库·时序数据库
2***65633 小时前
数据库操作与数据管理——Rust 与 SQLite 的集成
数据库·rust·sqlite
V***u4533 小时前
如何查看PostgreSQL的版本
数据库·postgresql
倔强的石头_3 小时前
openGauss向量数据库:赋能智能制造的工业AI实践
数据库