你真的懂 MySQL 一致性读和当前读的区别吗? 表结构遵循当前读?
itomcoil 2025-09-11 21:05 6 浏览
引言
本文章将逐步拆分讲解 ReadView,深入理解当前读和一致性读的区别。使用查询语句和更新语句逐步分析 MySQL 是如何进行事务的多版本并发控制的。我们以一个问题开始我们的讨论吧
begin/start transaction 命令代表了事务的开启吗?换句话说指向该命令的时候,会生成一个 ReadView 吗?
正文
首先需要解释的就是 begin/start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作的 InnoDB 表的语句,事务才真正启动。如果想要马上启动一个事务,需要使用 start transaction with consitent snapshot 命令。这里将的视图都是一致性视图:consistent read view,而不是 View 视图虚拟表
- begin/start transaction一致性视图是在第执行第一个快照读语句时创建的
- start transaction with consitent snapshot一致性视图是在执行该命令时创建的
接下来我们将 read view 拆开来看,进一步理解 MVCC,也就是“快照”在 MVCC 里是怎么工作的?
在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。注意,这个快照是基于整库的。InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的 而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。也就是说,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id,下图就是 undo log 引用链
其实在 InnoDB 的存储中,每一个行数据就是上图 V4 表格中的内容,包含了 trx_id 和 roll_pointeri 字段。而后面虚线连接的三个表格其实就是 undo log 中的内容。此外 V3,V2,V1 版本在数据库中并不是物理真是存在的,而是每次需要这个版本的数据时,去根据 roll_pointer 指针计算。例如 V3 版本的数据就是通过 V4 依次执行 U3 和 U2 命令计算而来的
所以,一个事务启动的时候,只需要声明当前事务 ID 就能够知道哪些数据是否可以读取。以当前事务启动为准,如果一个数据版本是在当前事务之前生成的,那么就认为可读。如果是当前事务之后才生成的,就说明不能够读这个版本,需要沿着 roll_pointer 指针找到之前的版本
我们继续理解一致性视图 ReadView。每一次生成 ReadView 时,都会记录下图中的四个值。其中活跃的事务 ID 列表就是指已经启动的但还未提交的事务
数据版本的可见性规则就是基于 trx_id 和 ReadView 的对比结果完成的,具体的流程如下
- 如果 trx_id 是当前的事务,那么就可以访问
- 如果事务 id 是活跃 id 之前就已经提交的事务,那么就可以访问
- 如果事务 id 是当前事务之后才开启的事务,那么就不可以访问
- 如果事务 id 处于活跃 id 集合中,有两种情况
- 4.1 活跃 id 已经提交,可以访问
- 4.2 活跃 id 未提交,不可以访问
通过时间节点(trx_id 的序号)来看就是下图的效果
读到这里,你就明白了“所有数据都有多个版本”的这个特性,以及为什么 MySQL 怎么实现“秒级创建快照”的能力
下面我们进一步用示例理解上面的流程,并进一步理解 Repeatable Read 和 Read Commit
当事务 104 执行第一个 select name from person where id = 1;,会生成一个 ReadView,如下
此时 undo log 的版本链如下
当查询的时候,会拿着 V2 版本的 trx_id,也就是 102 去和 ReadView 中的内容进行判断(逻辑上面讲过)
- 102 是否是create_trx_id?不是,继续判断
- 102 是否小于 101?不是,继续判断
- 102 是否大于 104?不是,继续判断
- 102 是否在m_ids中?不在,继续判断,只剩下最后一种可能
- 102 不在m_ids中,并且事务员已经提交 ==> 可以访问
当事务 104 执行第二个 select name from person where id = 1;,根据不同的隔离级别有不同的处理方式
- RC 级别,此时会在生成一个新的 ReadView。那么此时的 ReadView 是不是就和第一次不一样了(比如这一次生成的 ReadView 中,m_ids只有 101 了)?那整个查询的结果是不是可能就不一样了,这里就不再继续演示
- RR 级别,会继续沿用第一次生成的 ReadView,所以判断的结果是一样的!
说完了读,我们继续讲更新数据的原则,在下面的例子中(事务 C 采用的是自动提交模式),最后事务 A 查询的结果是 3,那么根据一致性读,结果似乎不正确?
在事务 B 的 update 语句中,事务 B 的活动是先生成的,之后事务 C 才提交,不是应该看不见 (1,2) 吗,怎么能算出 (1,3) 来?
其实如果事务 B 在更新之前查询一次数据,这个查询返回的 k 的值确实是 1。
但是,当它要去更新数据的时候,就不能再在历史版本上更新了,否则事务 C 的更新就丢失了。因此,事务 B 此时的 set k=k+1 是在(1,2)的基础上进行的操作
所以,这里就用到了这样一条规则:
更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)
因此,在更新的时候,当前读拿到的数据是 (1,2),更新后生成了新版本的数据 (1,3),这个新版本的 trx_id 是 101。所以,在执行事务 B 查询语句的时候,一看自己的版本号是 101,最新数据的版本号也是 101,是自己的更新,可以直接使用,所以查询得到的 k 的值是 3
当前读(Current Read),除了 update 语句外,select 语句如果加锁,也是当前读
所以,如果把事务 A 的查询语句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update,也都可以读到版本号是 101 的数据,返回的 k 的值是 3。下面这两个 select 语句,就是分别加了读锁(S 锁,共享锁)和写锁(X 锁,排他锁)
select k from t where id=1 lock in share mode;
select k from t where id=1 for update;
如果将事务 C 修改为下面的情况,事务 C 不是立马提交的,那么事务 B 又是如何处理 update 的?
这就要进一步提到“两阶段锁”。事务 C 没提交,也就是说 (1,2) 这个版本上的写锁还没释放。而事务 B 是当前读,必须要读最新版本,而且必须加锁,因此就被锁住了,必须等到事务 C 释放这个锁,才能继续它的当前读
编辑
截止到现在,我们已经把一致性读(Consistent Read)和当前读(Current Read)以及行锁串起来了
总结来说就是:
可重复读的核心就是一致性读(consistent read);而事务更新数据的时候,只能用当前读。如果当前的记录的行锁被其他事务占用的话,就需要进入锁等待。
为什么表结构不支持“可重复读”?
这是因为表结构没有对应的行数据,也没有 row trx_id,因此只能遵循当前读的逻辑。但是,MySQL 8.0 已经可以把表结构放在 InnoDB 字典里了,也许以后会支持表结构的可重复读
相关推荐
- python数据分析中你必须知道的陷阱和技巧
-
数据分析是一门既有趣又有挑战的技能,它可以帮助我们从海量的数据中提取有价值的信息,为决策提供支持。但是,数据分析也不是一件轻松的事情,它需要我们掌握一定的编程、统计、可视化等知识,同时也要注意避免一些...
- python常见五大坑及避坑指南_python解决什么问题
-
python是一门非常流行和强大的编程语言,但是也有一些容易让初学者或者不熟悉的人掉入的坑。这里列举了一些python常见五大坑,以及如何避免或者解决它们。缩进问题。python使用缩进来表示代码块,...
- 收藏!2022年国家职业资格考试时间表公布
-
人社部14日公布2022年度专业技术人员职业资格考试工作计划,包括中小学生教师资格、会计师、精算师、建造师等各项考试日期。其中,证券期货基金业从业人员资格各次考试地点不同,具体安排以相关行业协会考试公...
- 苹果mac系统必须安装python3_macbook安装python3.7
-
苹果mac系统必须安装python3苹果mac系统口碑很好,但不能像linux系统一样同时提供python2和python3环境,对程序员来说是非常不友善的。资深程序员都知道,Python3才是P...
- 通过python实现猴子吃桃问题_python小猴子吃桃的问题
-
1、问题描述:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,就只剩...
- python 中的第一个 hello world 程序输出
-
程序运行:print("helloworld")我使用的是Python程序3.7.0版本介绍下print概念print字面意思打印,将文本输出内容打印出来输入:print(&...
- 持久化 Python 会话:实现数据持久化和可重用性
-
Midjourney生成R语言会话持久化熟悉或常用R语言进行数据分析/数据挖掘/数据建模的数据工作者可能对R语言的会话保存和会话恢复印象比较深刻,它可以将当前session会话持久化保存,以便分...
- 如何将Python算法模型注册成Spark UDF函数实现全景模型部署
-
背景Background对于算法业务团队来说,将训练好的模型部署成服务的业务场景是非常常见的。通常会应用于三个场景:部署到流式程序里,比如风控需要通过流式处理来实时监控。部署到批任务中部署成API服...
- Python 字典l转换成 JSON_python转化字典
-
本文需要5分钟。如果对您有用可以点赞评论关注.Python字典到JSONJSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式,它基于ECMAScrip...
- [python] 基于PyOD库实现数据异常检测
-
PyOD是一个全面且易于使用的Python库,专门用于检测多变量数据中的异常点或离群点。异常点是指那些与大多数数据点显著不同的数据,它们可能表示错误、噪声或潜在的有趣现象。无论是处理小规模项目还是大型...
- 总结90条写Python程序的建议_python写程序的步骤
-
1.首先 建议1、理解Pythonic概念—-详见Python中的《Python之禅》 建议2、编写Pythonic代码 (1)避免不规范代码,比如只用大小写区分变量、使用容易...
- ptrade系列第六天:持久化处理2_持久化的三种状态
-
前一次跟大家分享了利用pickle进行策略数据的持久化。但是这种方式有个问题,就是保存下来的数据无法很直观的看到,比较不方便,所以今天给大家带来另一种方式,将数据通过json保存。importjso...
- Python数据持久化:JSON_python的json用法
-
编程派微信号:codingpy上周更新的《ThinkPython2e》第14章讲述了几种数据持久化的方式,包括dbm、pickle等,但是考虑到篇幅和读者等因素,并没有将各种方式都列全。本文将介绍...
- 干货 | 如何利用Python处理JSON格式的数据,建议收藏
-
作者:俊欣来源:关于数据分析与可视化JSON数据格式在我们的日常工作中经常会接触到,无论是做爬虫开发还是一般的数据分析处理,今天,小编就来分享一下当数据接口是JSON格式时,如何进行数据处理进行详...
- Python中Pyyaml模块的使用_python模块介绍
-
一、YAML是什么YAML是专门用来写配置文件的语言,远比JSON格式方便。YAML语言的设计目标,就是方便人类读写。YAML是一种比XML和JSON更轻的文件格式,也更简单更强大,它可以通过缩进来表...
- 一周热门
- 最近发表
- 标签列表
-
- ps图案在哪里 (33)
- super().__init__ (33)
- python 获取日期 (34)
- 0xa (36)
- super().__init__()详解 (33)
- python安装包在哪里找 (33)
- linux查看python版本信息 (35)
- python怎么改成中文 (35)
- php文件怎么在浏览器运行 (33)
- eval在python中的意思 (33)
- python安装opencv库 (35)
- python div (34)
- sticky css (33)
- python中random.randint()函数 (34)
- python去掉字符串中的指定字符 (33)
- python入门经典100题 (34)
- anaconda安装路径 (34)
- yield和return的区别 (33)
- 1到10的阶乘之和是多少 (35)
- python安装sklearn库 (33)
- dom和bom区别 (33)
- js 替换指定位置的字符 (33)
- python判断元素是否存在 (33)
- sorted key (33)
- shutil.copy() (33)