当前位置: 首页 > news >正文

MqSQL中的《快照读》和《当前读》

目录

1、MySQL读取定义

1.1、锁的分类

1.2、快照读与当前读

1.3、使用场景

1.4、区别

2、实现机制

2.1、实现原理

2.2、隔离级别和快照联系

1、隔离级别

2、快照读

2.3、快照何时生成

3、SQL场景实现

3.1、快照读

3.2、当前读

4、锁的细节(与当前读相关)

5、影响 / 并发行为

5.1、快照读:

5.2、当前读:

6、注意事项与常见误区


前言

        MySQL读取(主要指 InnoDB 存储引擎)中“快照读(snapshot read,也叫一致性读/consistent read)”和“当前读(current read)”。

如下所示:

        这两种读取方式在事务隔离级别、并发控制和数据一致性方面有着本质的区别。主要区别在于是否加锁以及数据一致性。


1、MySQL读取定义

1.1、锁的分类

1、共享锁(S锁):

   共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。

        如果事务T仅对数据A进行读取,那么会对数据A加上共享锁,之后则其他事务如果要读取数据A的话可以对其继续加共享锁,但是不能加排他锁(也就是无法修改数据)。获准共享锁的事务只能读数据,不能修改数据。

2、排他锁(X锁):

        用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。

        如果事务T对数据A要进行修改,则需要对其添加排它锁,加上排他锁后,则其他事务不能再对A加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据

SQL代码示例:

 共享锁
select * from table id = 1 lock in share mode;排他锁
select * from table where id = 1 for update;

1.2、快照读与当前读

1、快照读

        (Snapshot Read / Consistent Read)基于事务快照(MVCC),读取的是“某个时间点”的已提交版本,不加锁、非阻塞,多个读返回一致的历史版本。

 REPEATABLE READ 下(基于事务开始时的快照):同一事务内多次 SELECT 返回相同结果;

 READ COMMITTED 下每个语句建立新的快照。

SQL示例如下:

-- 事务 A
START TRANSACTION;
SELECT * FROM users WHERE id = 1; -- 快照读,读取事务开始时的数据-- 事务 B
START TRANSACTION;
UPDATE users SET name = 'Alice' WHERE id = 1; -- 更新数据并提交
COMMIT;-- 事务 A
SELECT * FROM users WHERE id = 1; -- 仍然读取事务开始时的数据(快照读)
COMMIT;

2、当前读

        (Current Read):直接读取当前最新数据(buffer pool/磁盘)并且读取之后还要保证其他并发事务不能修改当前记录,对读取的记录加锁。

当前读:select…lock in share mode,select…for update。

当前读:update,delete,insert。

SQL代码示例如下:

-- 事务 A
START TRANSACTION;
SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 当前读,获取最新数据并加锁-- 事务 B
START TRANSACTION;
UPDATE users SET name = 'Alice' WHERE id = 1; -- 被阻塞,等待事务 A 释放锁-- 事务 A
COMMIT; -- 提交后,事务 B 的更新操作继续执行

1.3、使用场景

1、当前读适用场景

  1. 需要获取最新数据的实时查询
  2. 需要保证数据一致性的金融交易
  3. 先读后写的业务逻辑
  4. 需要防止并发修改的场景

2、快照读适用场景

  1. 只读查询(报表、数据分析)
  2. 对实时性要求不高的查询
  3. 大查询,不希望阻塞其他操作
  4. 需要高并发的读场景

1.4、区别

如下所示:


2、实现机制

2.1、实现原理

1、快照读:通过MVCC+undolog实现;

        如果最新版本不是当前事务可见,InnoDB 会从 undo log 找到符合快照时间点的版本(所以快照读可能读取 undo 中的旧版本)。不会设置锁。

        不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log成为一条记录版本线性表,既链表,undo log的链首就是最新的旧记录,链尾就是最早的旧记录。

        所以快照读都是去读取undolog中链首的最新的旧记录。

更多mvcc和undolog的介绍,可参考:探讨LRU和MVCC在场景的实践-CSDN博客文章浏览阅读827次,点赞33次,收藏29次。MVCC 可以有效防止脏读,因为它提供一致的快照显示已提交修改的数据。它通过版本控制和快照隔离处理多用户并发访问,从而提高数据库的高并发性能。1、彻底拿下InnoDB的MVCC快照机制_innodb 的 快照读 如何提取快照-CSDN博客。 https://dyclt.blog.csdn.net/article/details/147588118?spm=1011.2415.3001.5331

2、当前读:通过共享锁+排他锁+Next-Key Lock实现;

当前读通过next-key锁(记录锁+间隙锁)来实现:行锁(Record Lock):锁定索引记录
间隙锁(Gap Lock):锁定索引记录之间的间隙
next-key锁:行锁+间隙锁,锁定一个范围并锁定记录本身
例如执行DELETE FROM T WHERE age = 7;时,MySQL会在age索引上加锁(4,10)区间,防止其他事务
插入age=7的记录,从而避免幻读问题。

如下所示:

  1. 每次对行数据进行读取的时候,加共享锁。此时就不允许修改,但是允许其他事务读取,所以每次都可以读到最新的数据。
  2. 每次对行数据进行修改的时候,加排他锁,不允许其他事务读取和修改。这种情况下其他事务读取的数据也一定是最新的数据。
  3. 每次对范围行数据进行读取的时候,对这个范围加一个范围共享锁
  4. 每次对范围行数据进行修改的时候,读这个范围加一个范围排它锁。
  5. 基于上述锁机制,实现当前读,确保每次读取的都是最新的数据。

⚠️注意:

        next-key包括两部分:行锁间隙锁。行锁是加在索引上的锁(而非数据行),间隙锁是加在索引之间的。

2.2、隔离级别和快照联系

1、隔离级别

1、读未提交(READ UNCOMMITTED)

快照读:不存在,总是读取最新数据(包括未提交的)。

当前读:读取最新已提交数据并加锁。

2、读已提交(READ COMMITTED)

快照读:每次SELECT都会生成新的ReadView,读取最新已提交数据。

当前读:读取最新已提交数据并加锁。

3、可重复读(REPEATABLE READ)

快照读:事务第一次SELECT时生成ReadView,后续复用,保证读取一致性。

当前读:读取最新已提交数据并加锁。

4、串行化(SERIALIZABLE)

快照读:退化为当前读(加共享锁)。

当前读:读取最新已提交数据并加锁。

2、快照读

REPEATABLE READ(默认):

        事务开始时建立一次快照,整个事务内普通 SELECT 都基于这个快照(repeatable)。

READ COMMITTED:

        每个语句单独建立快照(statement-level),因此同一事务内不同语句可能看到不同已提交数据(避免脏读,但允许不可重复读)。

Undo log 提供历史版本;如果旧版本被 purge 掉,读取老快照可能失败。

2.3、快照何时生成

1、在读未提交隔离级别下,快照是什么时候生成的?

没有快照,因为不需要,怎么读都读到最新的,不管是否提交。

2、在读已提交隔离级别下,快照是什么时候生成的?

SQL语句开始执行的时候。

3、在可重复读隔离级别下,快照是什么时候生成的?

        事务开始的时候(可能会有很多条select SQL语句执行,快照生命周期是到事务结束的时候)

4、在串行化隔离级别下,快照是什么时候生成的?

“写”会加“写锁”,“读”会加“读锁”,读的的数据都是当前最新的数据(没有快照,当前读)


3、SQL场景实现

3.1、快照读

1、 快照读不会看到后续提交(REPEATABLE READ):事务级快照读

事务 T1:

BEGIN;       SELECT value FROM t WHERE id=1; 

-- 假设值为 A (从快照读)(此时 T1 未提交,继续执行)

事务 T2:

BEGIN;       UPDATE t SET value='B' WHERE id=1;        COMMIT;

回到 T1:在 REPEATABLE READ 下仍然看到 A(快照不变)

SELECT value FROM t WHERE id=1;   

COMMIT;

2、 READ COMMITTED 下快照行为(每语句快照):语句级快照读

事务 T1:

BEGIN;     SELECT value FROM t WHERE id=1;

-- 看到 A

事务 T2:

BEGIN;       UPDATE t SET value='B' WHERE id=1;        COMMIT;

T1 再次执行:

SELECT value FROM t WHERE id=1;

-- 在 READ COMMITTED 下可能看到 B(新的语句快照)

3.2、当前读

1、当前读会立即看到并与写冲突/加锁:

事务 T1:

BEGIN;        SELECT value FROM t WHERE id=1 FOR UPDATE;

-- 当前读并对该行加排它锁(此时 T1 锁住该行)

事务 T2(并发执行):

BEGIN;   UPDATE t SET value='B' WHERE id=1;       COMMIT;

-- 将在 T1 提交前被阻塞,直到 T1 提交或回滚


4、锁的细节(与当前读相关)

更多关于mysql的锁介绍:MySQL的事务和锁机制的详细介绍_mysql锁机制与事物-CSDN博客文章浏览阅读1.3k次,点赞47次,收藏10次。MySQL事务与锁机制详解 摘要: 本文详细解析MySQL的事务特性和锁机制。首先介绍事务的ACID特性(原子性、一致性、隔离性、持久性)和四种隔离级别(读未提交、读提交、可重复读、串行化),重点分析多线程并发事务可能产生的脏读、不可重复读和幻读问题。其次深入探讨MVCC多版本并发控制原理及其解决并发问题的方式。然后系统讲解MySQL锁的分类,包括独占锁、共享锁及其兼容性,以及InnoDB引擎特有的记录锁、间隙锁、临键锁等实现细节。最后提供锁监控方法和优化建议,包括缩短事务长度、合理设计索引等,并比较不同隔_mysql锁机制与事物 https://dyclt.blog.csdn.net/article/details/149141972?spm=1011.2415.3001.5331

1、SELECT ... FOR UPDATE:

        对选中的记录加排它锁(record lock),在 REPEATABLE READ 下通常是 next‑key lock(record+gap),能防幻读;在 READ COMMITTED 下可能只加 record lock(实现差异)。

2、SELECT ... LOCK IN SHARE MODE:

共享锁,允许并发读取但阻止写入。

UPDATE/DELETE 语句本质是当前读(读取并锁定匹配行),而非快照读。


5、影响 / 并发行为

5.1、快照读:

不阻塞其他事务的写操作(不会加锁)。

不被子后续提交所影响(在 REPEATABLE READ 内)不会阻塞写者,但写者提交不会让已存在事务的快照变更。

5.2、当前读:

会加锁,可能阻塞其他事务(或被其他事务阻塞),用于实现悲观锁定和防止幻读/并发冲突。SELECT ... FOR UPDATE 会锁定读取到的记录(并可能使用 next-key lock 以避免幻读,取决于隔离级别和索引类型)。


6、注意事项与常见误区

1、 “快照读不会造成任何索引访问” 不准确:

        快照读也会走索引来定位行,但读取逻辑是基于版本可见性(从 undo 中回溯),它不设置锁。

2、长事务会保留较多 undo 数据:

影响 undo log 大小并增加 purge 压力;长时间的快照读(长事务)会阻止旧版本被清理。

3、不稳定结果

        与快照/当前读无直接关系,但与隔离级别和锁有关,使用 skip/limit 的分页在并发写入下可能产生。


总结

        普通 SELECT(无 FOR UPDATE/LOCK)是快照读(基于 MVCC,非锁定),在 REPEATABLE READ 下为事务级快照,同一事务内多次 SELECT 返回一致结果;READ COMMITTED 下每语句建立快照。

        SELECT ... FOR UPDATE / UPDATE / DELETE 等是当前读,会读取最新数据并加锁,可能阻塞其他事务写入。

        选择隔离级别与是否加锁,需要在“数据一致性需求(可重复读/防止幻读)”与“并发性能(锁竞争/阻塞)”之间权衡。


参考文章:

1、Mysql中的快照读和当前读_mysql快照读-CSDN博客文章浏览阅读1k次,点赞9次,收藏10次。本文介绍了MySQL的两种读取模式:当前读和快照读。当前读通过共享锁、排他锁和Next - Key Lock实现,每次读取最新数据;快照读通过MVCC和undolog实现,读写不冲突。还阐述了相关知识,如undolog、共享锁和排他锁,并分析了不同隔离级别下快照读的差异。 https://blog.csdn.net/aberwang9/article/details/135185331?ops_request_misc=&request_id=&biz_id=102&utm_term=mysql%E7%9A%84%E5%BD%93%E5%89%8D%E8%AF%BB%E5%92%8C%E5%BF%AB%E7%85%A7%E8%AF%BB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-135185331.142^v102^control&spm=1018.2226.3001.4187

2、【MYSQL】当前读和快照读_mysql 当前读-CSDN博客文章浏览阅读821次,点赞3次,收藏9次。复习下隔离级别:1、读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。2、读提交:一个事务提交之后,它做的变更会被其他事务看到3、可重复读:一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。未提交的数据对其他事务不可见4、串行化:对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。_mysql 当前读 https://blog.csdn.net/xiazi0721/article/details/141175805?ops_request_misc=&request_id=&biz_id=102&utm_term=mysql%E7%9A%84%E5%BD%93%E5%89%8D%E8%AF%BB%E5%92%8C%E5%BF%AB%E7%85%A7%E8%AF%BB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-141175805.142^v102^control&spm=1018.2226.3001.4187

http://www.lryc.cn/news/621874.html

相关文章:

  • Prometheus 监控 Kubernetes Cluster 最新极简教程
  • [论文笔记] WiscKey: Separating Keys from Values in SSD-Conscious Storage
  • DeepSeek-V2:一种强大、经济且高效的混合专家语言模型
  • 在 macOS 上顺利安装 lapsolver
  • 从根本上解决MAC权限问题(关闭sip)
  • vue3 wangeditor5 编辑器,使用方法
  • demo 通讯录 + 城市选择器 (字母索引左右联动 ListItemGroup+AlphabetIndexer)笔记
  • 分布式锁:从理论到实战的深度指南
  • 【机器人-基础知识】ROS常见功能架构
  • 微软自曝Win 11严重漏洞:可导致全盘数据丢失
  • Kafka生产者原理深度解析
  • 从ChatGPT到智能助手:Agent智能体如何颠覆AI应用
  • Python爬虫反爬检测失效问题的代理池轮换与请求头伪装实战方案
  • 【121页PPT】智慧方案智慧综合体智能化设计方案(附下载方式)
  • java + html 图片点击文字验证码
  • 结构体(Struct)、枚举(Enum)的使用
  • 电源测试系统ATECLOUD-Power,让您告别电源模块测试痛点!
  • MLOps已死,AgenticOps当立:构建新一代AI智能体的全新工程范式
  • 【Redis】Redis典型应用——分布式锁
  • 【部署K8S集群】 1、安装前环境准备配置
  • k8s1.28.2集群部署istioctl的1.20.0版本(X86架构)
  • Mac(一)常用的快捷键整理
  • Mac Mysql 卸载
  • 18- 网络编程
  • Java ArrayList的介绍及用法
  • 单片机闪烁灯实验
  • HDFS数据倾斜导致MapReduce作业失败的排查与优化实践
  • 3分钟解锁网页“硬盘“能力:离线运行VSCode的新一代Web存储技术
  • 【数据分享】2022 年黑龙江省小麦、玉米和水稻幼苗影像数据集
  • 经典回顾:Hive执行原理、MapReduce执行流程、Spark执行流程