【MySQL 读书笔记】RR(REPEATABLE-READ)事务隔离详解
这篇我觉得有点难度,我会更慢的更详细的分析一些 case 。
MySQL 的默认事务隔离级别和其他几个主流数据库隔离级别不同,他的事务隔离级别是 RR(REPEATABLE-READ) 其他的主流数据库比如 oracle 通常是 RC(READ-COMMITTED)
关于数据库有哪些隔离级别我这里就不详细阐述了,大概是什么特性我这里就不阐述了大家可以自行翻阅资料,让我们聚焦这两个最重要的隔离级别在一些查询更新的时候会出现什么样的特性表达。
当我们使用 RR 的时候,事务启动的时候会创建一个视图 read-view,之后事务执行期间,即使有其他事务修改了数据,事务看到的仍然和她启动的时候看到的一样。也就是说,一个在可重复读隔离级别下执行的事务不受外界影响。
但是上一篇分享锁的文章里面我们也提到了,如果说另外一个事务对表加了行锁,他会被锁住进入等待状态。那么当等待状态结束,这个事务自己要获取行锁更新数据的时候,他读到的值是什么呢?
来看个例子
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);
然后使用这个事务启动顺序来测试
这里有几个点需要注意,我们在数据库使用事务 begin/start transaction 命令并不是一个事务的起点,在执行到第一个操作 InnooDB 表的语句,事务才被真正启动。
如果我们要马上启动一个一致性读事务使用 start transaction with consistent snapshot 这个。
它的含义是:执行 start transaction 同时建立本事务一致性读的 snapshot . 而不是等到执行第一条语句时,才开始事务,并且建立一致性读的 snapshot 。
文章中说,这个顺序查询,事务 B 查询到的 id =1 的 k 值是3,事务 A 查询到的值 k 值是 1。我起初也无法理解,下面让我们一步一步来得出结论。
快照”在 MVCC 里是怎么工作的?
在可重复读隔离级别下,事务在启动的时候就拍了快照。InnoDB 里面每个事务有一个唯一的事务 ID, 叫 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的。是按照申请顺序递增的。每行数据也有多个版本,每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID, row trx_id。旧的数据版本要保留,并且新的数据版本中,能够有信息可以直接拿到。
当前最新版本是 V4 V4 版本是经过一系列更新之后得到的最新的状态。 他的 row trx_id = 25。
u1 u2 u3 都是 undo log 的记录,我们可以在 v4 通过 undolog 恢复到版本v1 v2 v3 并不是物理上真实存在的。
这里按照可重复度的定义,当一个事务启动的时候,能偶看到所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。
因此一个在 RR 事务级别启动一个事务的时候声明说,以我启动的时刻为准,如果一个数据版本是在我启动之前生成的就认,如果是我启动之后才生成的,就不认,我必须找到他的上一个版本。如果上一个版本也不可见,就继续往前找。当然如果是这个事务自己本身更新的数据,它自己是要认的。
在实现上, InnoDB 为每个事务构造了一个数组,用来保存这个事务启动的时候,当前正在“活跃”的所有事务 ID,“活跃”指的是,启动了但是还没有提交。
这个数组组成了一个类似这样的东西
低水位:指获取到这个数组内的 trx_id 最小值。
高水位:指获取到的这个数组内的 trx_id 最大值 + 1
这样对于当前事务启动的瞬间来说,一个数据版本的 row trx_id 有以下几种可能。
1. 如果落在绿色部分,标示这个版本是已经提价的事务护着当前自己事务生成的,这个数据是可见的。
2. 如果落在红色部分,标示这个版本是由将来的事务生成的,是不可见的。
3. 如果落在黄色部分,
a. 如果 row trx_id 在数组中,标示这个版本是由还没有提交哦的事务生成的,不可见。
b. 如果 row trx_id 不在数组中,标示这个版本是已经提交了的事务生成的,可见。
下面我们用上面的理论拉来解释一下为什么我第一张图的查询结果会是那个样子。
1. 假设 事务 A 开始前,系统里面没有哦活跃事务 ID .
2. 事务 A 开始时候的事务版本号为 100 事务 B 开始时候的事务版本号为 101 事务 C 开始时候的事务版本号为 102。
3. 三个事务开始前 id =1 k =1 的数据 row trx_id 是 90.
事务 A 开始的时候事务数组为 [100]
事务 B 开始时候的事务数组为 [100, 101]
事务 C 开始时候的事务数组为 [100, 101, 102]
祖先版本是 id =1, k=2 对应版本是 90。
第一个有效更新的事务是 C 当 C 完成更新之后 102 版本就是对应 id = 1 k = 2.
这个时候由于版本 102 无论对于事务 B 还是 事务 A 都处于高水位,所以都是不见的。也就是说现在我们执行
select * from t where id = 1 会发现
mysql> select * from t;
+----+------+
| id | k |
+----+------+
| 1 | 1 |
| 2 | 2 |
+----+------+
这个时候第二个有效事务 B 更新了,把数据从 id =1 k = 2 变更为 id =1 k=3,这个时候数据的最新版本变成了 101,而102 变成了历史版本。
注意这个时候事务 B 会发现自己的数据没有经过 k=2 这一步 直接就变成 k =3 了。。。因为事务 C 更新并且提交了,我们在这个基础上增加会读取到 102 的更新。
但是事务 A 还是无法读取到 102 版本和 101 版本的更新,因为他们都在高水位,所以最终读取到的还是 id =1 k=1。
但是真实的情况 事务 A 是会去判断的,也就是说他会找到最后一个被更新的版本 101 会发现是高水位不可见。
接着找上一个版本 102 还是高水位不可见。
最后找到原始版本 90 处于低于低水位的区域可见。
这样执行下来,虽然期间这一行数据被修改过,但是事务 A 无论在什么时候查询,看到这行数据的结果都是一致的,所以我们称为一致性读。
RR 的更新数据都是先读后写的,这个读就是当前读。这可以解释为什么我们可以跳过 k=2 直接 k=3。因为在 k=1 的时候进行当前读发现 k=2 了,然后再 +1 就 k=3 了。
当然 select xx for update | lock in share mode 也是当前读。
我觉得 此片文章到此就差不多了。感觉老师后面以紧接着介绍了一些无关紧要的东西 包括 RC 的情况。给本来就比较难以理解的情况搞得更复杂了。
现在我解除到大部分公司的 DB 使用 MySQL 都会将事务隔离级别从默认的 RR 设置到 RC,更好理解也可以更方便的用乐观锁来保证数据的一致性。并且我感觉如果不使用当前读,可能还会对性能有一定的影响。毕竟上面介绍到的流程里面,是需要扫 undolog 参与的,感觉这些可能都会有一定的性能损失。
Reference:
本读书笔记皆来自发布在极客时间的 林晓斌(丁奇)的 MySQL 实战45讲:
极客时间版权所有: https://time.geekbang.org/ 版权所有:
https://time.geekbang.org/column/article/70562
【MySQL 读书笔记】RR(REPEATABLE-READ)事务隔离详解的更多相关文章
- Net Core中数据库事务隔离详解——以Dapper和Mysql为例
Net Core中数据库事务隔离详解--以Dapper和Mysql为例 事务隔离级别 准备工作 Read uncommitted 读未提交 Read committed 读取提交内容 Repeatab ...
- MySQL提升笔记(3)日志文件详解
在MySQL数据库和InnoDB存储引擎中,有很多种文件,如:参数文件.日志文件.socket文件.pid文件.MySQL表结构文件.存储引擎文件. 本节重点关注日志文件,MySQL的复制.事务等重要 ...
- Windows驱动——读书笔记《Windows驱动开发技术详解》
=================================版权声明================================= 版权声明:原创文章 谢绝转载 请通过右侧公告中的“联系邮 ...
- (转)Spring事务管理详解
背景:之前一直在学习数据库中的相关事务,而忽略了spring中的事务配置,在阿里面试时候基本是惨败,这里做一个总结. 可能是最漂亮的Spring事务管理详解 https://github.com/Sn ...
- 【Spring】——声明式事务配置详解
项目中用到了spring的事务: @Transactional(rollbackFor = Exception.class, transactionManager = "zebraTrans ...
- 可能是最漂亮的Spring事务管理详解
Java面试通关手册(Java学习指南):https://github.com/Snailclimb/Java_Guide 微信阅读地址链接:可能是最漂亮的Spring事务管理详解 事务概念回顾 什么 ...
- 可能是最漂亮的Spring事务管理详解 专题
微信阅读地址链接:可能是最漂亮的Spring事务管理详解 事务概念回顾 什么是事务? 事务是逻辑上的一组操作,要么都执行,要么都不执行. 事物的特性(ACID): 原子性: 事务是最小的执行单位,不允 ...
- 最漂亮的Spring事务管理详解
SnailClimb 2018年05月21日阅读 7245 可能是最漂亮的Spring事务管理详解 Java面试通关手册(Java学习指南):github.com/Snailclimb/- 微信阅读地 ...
- Redis的事务功能详解
Redis的事务功能详解 MULTI.EXEC.DISCARD和WATCH命令是Redis事务功能的基础.Redis事务允许在一次单独的步骤中执行一组命令,并且可以保证如下两个重要事项: >Re ...
随机推荐
- Elasticsearch之索引模板index template与索引别名index alias
为什么需要索引模板? 在实际工作中针对一批大量数据存储的时候需要使用多个索引库,如果手工指定每个索引库的配置信息(settings和mappings)的话就很麻烦了. 所以,这个时候,就存在创建索引模 ...
- ZooKeeper 02 - ZooKeeper集群的节点为什么是奇数个
目录 1 关于节点个数的说明 2 ZooKeeper集群的容错数 3 ZooKeeper集群可用的标准 4 为什么不能是偶数个节点 4.1 防止由脑裂造成的集群不可用 4.2 奇数个节点更省资源 4. ...
- Java进阶篇设计模式之四 -----适配器模式和桥接模式
前言 在上一篇中我们学习了创建型模式的建造者模式和原型模式.本篇则来学习下结构型模式的适配器模式和桥接模式. 适配器模式 简介 适配器模式是作为两个不兼容的接口之间的桥梁.这种类型的设计模式属于结构型 ...
- [开发技巧]·HTML检测输入已完成自动填写下一个内容
[开发技巧]·HTML检测输入已完成自动填写下一个内容 个人网站 --> http://www.yansongsong.cn 在上一个博客中简易实现检测输入已完成,我们实现了检测输入已完成,现在 ...
- 让ASP.NET Core支持GraphQL之-GraphQL的实现原理
众所周知RESTful API是目前最流行的软件架构风格之一,它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制. RESTful的优越性是毋庸置疑 ...
- mysql-8.0 安装教程(自定义配置文件,密码方式已修改)
下载zip安装包: MySQL8.0 For Windows zip包下载地址:https://dev.mysql.com/downloads/file/?id=476233,进入页面后可以不登录.后 ...
- APP网站安全漏洞检测服务的详细介绍
01)概述: 关于APP漏洞检测,分为两个层面的安全检测,包括手机应用层,以及APP代码层,与网站的漏洞检测基本上差不多,目前越来越多的手机应用都存在着漏洞,关于如何对APP进行漏洞检测,我们详细的介 ...
- C# 中一些类关系的判定方法
1. IsAssignableFrom实例方法 判断一个类或者接口是否继承自另一个指定的类或者接口. public interface IAnimal { } public interface ID ...
- 基于python的种子搜索网站-开发过程
本讲会对种子搜索网站的开发过程进行详细的讲解. 源码地址:https://github.com/geeeeeeeek/bt 项目开发过程 项目简介 该项目是基于python的web类库django开发 ...
- Ubuntu16.04下搭建mysql + uwsgi + nginx环境启动flask 项目
1.安装mysql Sudo apt-get install mysql 配置mysql的数据存储路径,默认在 /var/lib/mysql sudo cp -R /var/lib/mysql/* / ...