当前位置:首页 > 文章列表 > 数据库 > MySQL > MySQL 一致性快照怎样影响长事务读

MySQL 一致性快照怎样影响长事务读

来源:17golang原创 2026-10-17 22:18:29 0浏览 收藏

MySQL 一致性快照怎样影响长事务读

在 InnoDB 的 REPEATABLE READ(RR)隔离级别下,长事务中的普通一致性读通常沿用首次一致性读建立的快照,因此其他事务后来提交的新值,不会自动出现在后续普通 SELECT 中。普通 START TRANSACTION 不等于当场创建快照;若使用 WITH CONSISTENT SNAPSHOT,则可在 RR 下主动建立。READ COMMITTED(RC)则每次一致性读采用新的快照,不保证整段长报表使用同一视图。

下面用一个报表持续显示旧数量的假设案例还原原因。所有 SQL 均为原创教学示例,本文制作时未运行数据库,也没有实际事故时间、监控数据或性能结论。适用范围为 MySQL 8.4、InnoDB 和普通非锁定 SELECT。

官方文档:https://dev.mysql.com/doc/refman/8.4/en/innodb-consistent-read.html;事务启动语法:https://dev.mysql.com/doc/refman/8.4/en/commit.html。

长报表显示旧数量,影响的是新鲜度而非必然读错

设想报表事务 A 连续读取同一条数量记录,业务事务 B 已经提交修改,但报表仍显示原来的数。若 A 使用 RR 且已有一致性读快照,这可能正是隔离级别提供的稳定视图,不应立即认定为缓存失效或提交丢失。

排查前要记录同一连接上的事务开始点、首次普通读、B 的提交点,以及 A 是否已经结束。只记录请求进入应用的时间不够:应用可能较早打开事务,也可能由框架先发出了一次查询。反过来,事务先开始但还没进行一致性读时,稍后第一次查询可能看见事务开始之后、查询之前已提交的数据。

用两个会话还原首次读取与提交的先后

请只在独立测试库执行下面的准备语句;表名必须尚不存在,若已存在就换一个新的测试表名并同步修改全部示例,不覆盖现有数据。需要两个连接到同一实例、同一测试库的会话 A 与 B,且没有其他写入者。不要使用临时表,因为另一个会话不能共享它。

-- 在测试库准备普通 InnoDB 表,仅执行一次。
CREATE TABLE snapshot_read_demo (
    id INT PRIMARY KEY,
    qty INT NOT NULL
) ENGINE=InnoDB;
INSERT INTO snapshot_read_demo VALUES (1,100), (2,200), (3,300);
COMMIT;

第一轮使用 id=1。按下面注释逐段切换会话,不要整块复制到一个窗口运行。A 在首次查询前不执行任何其他 InnoDB 普通查询。

-- 会话 A:只设置下一个事务为 RR,然后开始;此时尚未普通读。
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;

-- 切到会话 B:提交第一次变化。
START TRANSACTION;
UPDATE snapshot_read_demo SET qty=110 WHERE id=1;
COMMIT;

-- 切回 A:首次一致性读,预期 qty 为 110。
SELECT qty FROM snapshot_read_demo WHERE id=1;

-- 切到 B:提交第二次变化。
START TRANSACTION;
UPDATE snapshot_read_demo SET qty=120 WHERE id=1;
COMMIT;

-- 切回 A:仍使用原快照,预期依旧为 110;随后结束事务。
SELECT qty FROM snapshot_read_demo WHERE id=1;
COMMIT;

这一轮有两个观察点:A 的第一次读取看见 110,说明普通事务开始时间不等于快照创建时间;第二次仍是 110,说明快照建立以后,B 再提交不会自动刷新 A 的普通一致性读。所有数值都是按给定顺序推导的预期,不是运行输出。

显式快照生效,还需要隔离级别这个前提

第二轮使用尚未修改的 id=2,将 A 的启动语句改为显式快照。RR 下,它的作用相当于开始事务后立即建立一致性读视图,所以不能再把建立时间推迟到业务第一条 SELECT。

-- 会话 A:明确 RR,主动建立快照。
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION WITH CONSISTENT SNAPSHOT;

-- 切到会话 B:A 的快照建立后才修改并提交。
START TRANSACTION;
UPDATE snapshot_read_demo SET qty=210 WHERE id=2;
COMMIT;

-- 切回 A:普通 SELECT 预期仍是 200。
SELECT qty FROM snapshot_read_demo WHERE id=2;
COMMIT;

WITH CONSISTENT SNAPSHOT 不会替你修改隔离级别。在其他隔离级别下,该修饰子会被忽略并产生警告,不能指望在 RC 下靠它固定整段事务的快照。若对行为有疑问,可在启动语句后立即查看 SHOW WARNINGS;不要等别的语句覆盖了诊断信息再回看。

RR和RC与各自快照策略的静态对应结构图
图一:隔离级别与快照策略的对应关系。结构图不表现操作先后,实际顺序以正文会话示例为准。

根因判断只适用于一致性读,不是所有 SQL

这里的“同一快照”指普通非锁定 SELECT 的可见性。FOR UPDATE、FOR SHARE 和 UPDATE 等语句不能简单套用同一结论,也不应为了让报表看到新值就随手加锁。它们有不同的读取与锁定语义,可能改变并发行为。

当前事务自身之前写入的变化也是重要例外:同一事务会看见自己的修改。因此不要一边让 A 更新记录,一边用“A 的全部结果永远等于旧快照”来解释实验。本例特意让 A 只读、B 写入,保持比较条件清楚。显式快照也不是为每张表复制一份实体数据文件,不需要用存储复制来理解可见性。

普通一致性读与其他读取行为的边界结构图
图二:普通读的快照依据与需要另行解释的语句分组。连线表示语义关联,不是执行流程或真实数据库截图。

修复动作要先选择稳定视图还是逐次更新

如果报表要求多条查询在同一历史视图下相互一致,RR 的旧读可能是期望行为,此时应保留快照并向用户标明报表基准,而不是在中间强制刷新。若业务希望每条查询看见执行时已提交的新变化,可以评估 RC,但要接受前后结果可能不同。

第三轮用 id=3 对照 RC,仍只影响 A 的下一个事务,不修改全局配置。

-- 会话 A:第一次读,预期为 300。
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
SELECT qty FROM snapshot_read_demo WHERE id=3;

-- 切到 B:在 A 两次读取之间提交。
START TRANSACTION;
UPDATE snapshot_read_demo SET qty=310 WHERE id=3;
COMMIT;

-- 切回 A:新的一致性读快照,预期为 310。
SELECT qty FROM snapshot_read_demo WHERE id=3;
COMMIT;

另一种做法是在明确的业务边界结束旧事务,再开始新事务。对已有写入的事务,提交意味着正式保存,回滚意味着放弃,不能为了刷新视图盲目选择其中一个。重新发 START TRANSACTION 也不是安全的“清快照”操作:它可能隐式结束当前事务,必须先处理事务原本的业务责任。

防止事务边界被等待与连接复用拖长

应用应让事务只包住确有一致性要求的数据库操作,不把用户等待、网络调用和长时间计算顺手放进去。记录连接标识、事务边界与首条业务查询,异常路径显式结束事务,连接归还池前遵循驱动或框架的事务清理约定。不要只凭一次查询耗时短就断定整个事务很短。

模式普通一致性读的快照口径适用取舍
RR 普通事务首次一致性读建立并复用同事务稳定读取
RR 显式快照启动修饰子主动建立需要明确快照起点
RC每次一致性读新建接受前后值变化

事务开始很早,第一次查询为什么反而读到新值?

普通 RR 事务可能直到首次一致性读才建立快照;要确认之前是否真的没有其他一致性读。

提交另一个连接,能刷新当前 RR 事务吗?

不能据此刷新已建立的快照。是否需要结束当前事务并重新读取,应由业务一致性边界决定。

READ ONLY 会自动等于固定快照吗?

不会。读写访问模式与隔离级别、快照建立时机是不同维度;只读声明不能替代 RR 或显式快照语义。

这个假设案例的结论是:先对齐首次一致性读与并发提交的时机,再判断旧值是否异常。长事务不是“所有数据冻结在 BEGIN 时刻”的同义词,隔离级别、读类型和显式快照缺一项都可能改变解释。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
MySQL 查询计划选择异常时怎样读取 optimizer traceMySQL 查询计划选择异常时怎样读取 optimizer trace
上一篇
MySQL 查询计划选择异常时怎样读取 optimizer trace
MySQL 行锁队列怎样用跳过锁实现取件
下一篇
MySQL 行锁队列怎样用跳过锁实现取件
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    507次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    569次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    593次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    546次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    364次使用