MVCC实现原理之ReadView(一步到位)(上)

简介: MVCC实现原理之ReadView(一步到位)

前置知识点

MVCC 的实现依赖于:隐藏字段、Undo Log、Read View。

什么是ReadView

Read View是一个数据库的内部快照,该快照被用于InnoDB存储引擎中的MVCC机制。简单点说,Read View就是一个快照,保存着数据库某个时刻的数据信息。Read View会根据事务的隔离级别决定在某个事务开始时,该事务能看到什么信息。就是说通过Read View,事务可以知道此时此刻能看到哪个版本的数据记录(有可能不是最新版本的,也有可能是最新版本的)。可重复读、读已提交、读未提交,这几个隔离级别都会使用Read View。

设计思路

使用 READ UNCOMMITTED 隔离级别的事务,由于可以读到未提交事务修改过的记录,所以直接读取记录 的最新版本就好了。


使用 SERIALIZABLE 隔离级别的事务,InnoDB规定使用加锁的方式来访问记录。


使用 READ COMMITTED 和 REPEATABLE READ 隔离级别的事务,都必须保证读到 已经提交了的 事务修改 过的记录。假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,核心问 题就是需要判断一下版本链中的哪个版本是当前事务可见的,这是ReadView要解决的主要问题。


这个ReadView中主要包含4个比较重要的内容,分别如下:


1. creator_trx_id ,创建这个 Read View 的事务 ID。


说明:只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为 事务分配事务id,否则在一个只读事务中的事务id值都默认为0。


2. trx_ids ,表示在生成ReadView时当前系统中活跃的读写事务的 事务id列表 。  


3. up_limit_id ,活跃的事务中最小的事务 ID。


4. low_limit_id ,表示生成ReadView时系统中应该分配给下一个事务的 id 值。low_limit_id 是系 统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。


注意:low_limit_id并不是trx_ids中的最大值,事务id是递增分配的。比如,现在有id为1,


2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,


trx_ids就包括1和2,up_limit_id的值就是1,low_limit_id的值就是4。


ReadView的规则


有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见。


如果被访问版本的trx_id属性值与ReadView中的 creator_trx_id 值相同,意味着当前事务在访问 它自己修改过的记录,所以该版本可以被当前事务访问。


如果被访问版本的trx_id属性值小于ReadView中的 up_limit_id 值,表明生成该版本的事务在当前 事务生成ReadView前已经提交,所以该版本可以被当前事务访问。


如果被访问版本的trx_id属性值大于或等于ReadView中的 low_limit_id 值,表明生成该版本的事 务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。



如果被访问版本的trx_id属性值在ReadView的 up_limit_id 和 low_limit_id 之间,那就需要判 断一下trx_id属性值是不是在 trx_ids 列表中。


如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。


如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。


MVCC整体操作流程


了解了这些概念之后,我们来看下当查询一条记录的时候,系统如何通过MVCC找到它:


1. 首先获取事务自己的版本号,也就是事务 ID;


2. 获取 ReadView;


3. 查询得到的数据,然后与 ReadView 中的事务版本号进行比较;


4. 如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照;


5. 最后返回符合规则的数据。 在隔离级别为读已提交(Read Committed)时,一个事务中的每一次 SELECT 查询都会重新获取一次Read View。


如表所示:


334da40626754d20a6de825fcdb5a05a.png

注意,此时同样的查询语句都会重新获取一次 Read View,这时如果 Read View 不同,就可能产生 不可重复读或者幻读的情况。  


当隔离级别为可重复读的时候,就避免了不可重复读,这是因为一个事务只在第一次 SELECT 的时候会 获取一次 Read View,而后面所有的 SELECT 都会复用这个 Read View,如下表所示:  

2217e46b0ebe475cb1a5b3033bbfdd36.png

举例说明

READ COMMITTED隔离级别

READ COMMITTED :每次读取数据前都生成一个ReadView。

现在有两个 事务id 分别为 10 、 20 的事务在执行:

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...

此刻,表student 中 id 为 1 的记录得到的版本链表如下所示:  

ddd1f91d737c4518accaa2099665b53b.png

假设现在有一个使用 READ COMMITTED 隔离级别的事务开始执行:  

# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 10、20未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'张三'

之后,我们把 事务id 为 10 的事务提交一下:  

# Transaction 10
BEGIN;
UPDATE student SET name="李四" WHERE id=1;
UPDATE student SET name="王五" WHERE id=1;
COMMIT;

然后再到 事务id 为 20 的事务中更新一下表 student 中 id 为 1 的记录:

# Transaction 20
BEGIN;
# 更新了一些别的表的记录
...
UPDATE student SET name="钱七" WHERE id=1; 
UPDATE student SET name="宋八" WHERE id=1;

此刻,表student中 id 为 1 的记录的版本链就长这样:


3dcf5021ea854a4896f1e0b4f6468365.png

然后再到刚才使用 READ COMMITTED 隔离级别的事务中继续查找这个 id 为 1 的记录,如下:  


# 使用READ COMMITTED隔离级别的事务
BEGIN;
# SELECT1:Transaction 10、20均未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'张三'
# SELECT2:Transaction 10提交,Transaction 20未提交
SELECT * FROM student WHERE id = 1; # 得到的列name的值为'王五'


相关文章
|
Java
JVM之本地内存以及元空间,直接内存的详细解析
JVM之本地内存以及元空间,直接内存的详细解析
1419 0
|
存储 算法 NoSQL
还分不清 Cookie、Session、Token、JWT?看这一篇就够了
Cookie、Session、Token 和 JWT(JSON Web Token)都是用于在网络应用中进行身份验证和状态管理的机制。虽然它们有一些相似之处,但在实际应用中有着不同的作用和特点,接下来就让我们一起看看吧,本文转载至http://juejin.im/post/5e055d9ef265da33997a42cc
50107 14
|
存储 缓存 监控
美团面试:说说OOM三大场景和解决方案? (绝对史上最全)
小伙伴们,有没有遇到过程序突然崩溃,然后抛出一个OutOfMemoryError的异常?这就是我们俗称的OOM,也就是内存溢出 本文来带大家学习Java OOM的三大经典场景以及解决方案,保证让你有所收获!
6679 2
美团面试:说说OOM三大场景和解决方案? (绝对史上最全)
|
前端开发 测试技术 对象存储
Grafana Loki查询加速:如何在不添加资源的前提下提升查询速度
Grafana Loki查询加速:如何在不添加资源的前提下提升查询速度
594 59
深入解析 JMeter TPS 测试:从理论到实践
本文档介绍了如何使用Apache JMeter进行TPS测试。TPS(Transactions Per Second)是衡量系统性能的关键指标,表示每秒处理的事务数。在JMeter中,创建测试计划包括配置线程组、HTTP请求、定时器和监听器。运行测试后,通过聚合报告分析吞吐量、平均响应时间和错误率来计算TPS。当TPS不足时,可优化服务器资源、应用程序代码、数据库查询或引入缓存来提升性能。掌握TPS测试有助于系统性能优化。
|
缓存 监控 安全
Spring AOP 详细深入讲解+代码示例
Spring AOP(Aspect-Oriented Programming)是Spring框架提供的一种面向切面编程的技术。它通过将横切关注点(例如日志记录、事务管理、安全性检查等)从主业务逻辑代码中分离出来,以模块化的方式实现对这些关注点的管理和重用。 在Spring AOP中,切面(Aspect)是一个模块化的关注点,它可以跨越多个对象,例如日志记录、事务管理等。切面通过定义切点(Pointcut)和增强(Advice)来介入目标对象的方法执行过程。 切点是一个表达式,用于匹配目标对象的一组方法,在这些方法执行时切面会被触发。增强则定义了切面在目标对象方法执行前、执行后或抛出异常时所
17861 4
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
2325 15
|
设计模式 缓存 Java
Spring监听器用法与原理详解(带ApplicationListener模型图)(1)
Spring监听器用法与原理详解(带ApplicationListener模型图)
1755 0
|
存储 监控 数据库
什么是聚集索引和非聚集索引?
【8月更文挑战第3天】
9435 6
|
负载均衡 应用服务中间件 数据安全/隐私保护
docker swarm 创建 Swarm 模式下的网络
【10月更文挑战第14天】
433 6

热门文章

最新文章