mysql从零单排之快照读与当前读的实现

 更新时间:2026年04月23日 11:15:34   作者:白晓虾  
本文主要介绍了mysql从零单排之快照读与当前读的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

了解完MVCC会不会有这样的疑问:MVCC实现了读不阻塞写,写不阻塞读的高效并发控制,写的时候读是不是拿到实时数据,读的时候可能拿到的不是实时写的数据?

关键区分:两种读操作

MySQL InnoDB 实际上有两种读模式,MVCC 只适用于其中一种:

读类型名称实现方式是否阻塞写数据一致性
快照读Snapshot Read (Consistent Read)MVCC,读历史版本不阻塞事务开始时的快照
当前读Current Read (Locking Read)加锁读最新版本会阻塞最新已提交数据
-- 快照读(普通 SELECT)-- 使用 MVCC
SELECT * FROM user WHERE id = 1;
-- 当前读(加锁 SELECT)-- 不使用 MVCC,直接读最新版本
SELECT * FROM user WHERE id = 1 FOR UPDATE;   -- 排他锁
SELECT * FROM user WHERE id = 1 LOCK IN SHARE MODE;  -- 共享锁

你的问题:

答案取决于业务场景:

场景1:读历史版本就够了(大多数场景)

银行查询账户余额(只读场景):

事务A(转账):UPDATE account SET balance = 900 WHERE id = 1;  -- 扣100
事务B(查询):SELECT balance FROM account WHERE id = 1;        -- 读快照

├─► 事务B 读到的 1000元 是"过时"的吗?技术上是的
├─► 但这是事务B 启动时的准确快照,业务上完全合理
└─► 用户只是查余额,不需要看到实时变化(实时变化可能还在处理中)

场景2:必须读最新数据(需要当前读)

库存扣减(不能超卖):

事务A:UPDATE stock SET count = count - 1 WHERE id = 1;  -- 剩9件
事务B:SELECT count FROM stock WHERE id = 1 FOR UPDATE;   -- 必须知道最新值!

├─► 事务B 用 FOR UPDATE,强制读最新版本(当前读)
├─► 如果 count 已经是 0,事务B 就知道卖完了
└─► 这会阻塞直到事务A提交,但保证了业务正确性

一致性模型对比

┌─────────────────────────────────────────────────────────────┐
│                    一致性光谱                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  最终一致性 ◄────────────────────────────► 强一致性          │
│                                                             │
│  快照读(MVCC)                    当前读(加锁)                │
│  ├─ 读历史版本                    ├─ 读最新版本               │
│  ├─ 可能"过时"但自洽               ├─ 实时但可能阻塞          │
│  └─ 适合:报表、查询               └─ 适合:金融交易、库存    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

为什么 MVCC 这样设计?

┌─────────────────────────────────────────────────────────┐
│  数据库设计哲学:不同场景需要不同的一致性级别               │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  场景1:淘宝商品列表页                                   │
│  ├─ 100万人同时浏览                                     │
│  ├─ 库存数字差几个没关系                                  │
│  └─ 用 MVCC 快照读 → 高并发,不阻塞商家改库存              │
│                                                         │
│  场景2:下单扣库存                                       │
│  ├─ 必须精确知道当前库存                                  │
│  ├─ 不能超卖                                            │
│  └─ 用当前读 FOR UPDATE → 阻塞等待,保证正确性             │
│                                                         │
│  同一系统,两种策略,各取所需                              │
│                                                         │
└─────────────────────────────────────────────────────────┘

代码示例对比

-- 假设表:account(id, balance)
┌─────────────────────────────────────────────────────────────┐
│  事务A(转账)                                                │
├─────────────────────────────────────────────────────────────┤
│  START TRANSACTION;                                          │
│  -- 扣减转账账户                                              │
│  UPDATE account SET balance = balance - 100 WHERE id = 1;    │
│  -- 此时 id=1 的 balance 内存中有新版本,但未提交              │
│                                                              │
│  -- 做一些其他处理(耗时较长)                                  │
│  ...                                                         │
│                                                              │
│  COMMIT;                                                     │
└─────────────────────────────────────────────────────────────┘
                              │
                              │ 并发执行
                              ▼
┌─────────────────────────────────────────────────────────────┐
│  事务B(查询余额)- 快照读                                     │
├─────────────────────────────────────────────────────────────┤
│  START TRANSACTION;                                          │
│  SELECT balance FROM account WHERE id = 1;  -- 读快照        │
│  -- 结果:1000(事务A修改前的值)                             │
│  -- 原理:TRX_ID 判断,事务A未提交,对事务B不可见               │
│  COMMIT;                                                     │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│  事务C(必须确认转账结果)- 当前读                              │
├─────────────────────────────────────────────────────────────┤
│  START TRANSACTION;                                          │
│  SELECT balance FROM account WHERE id = 1 FOR UPDATE;        │
│  -- 结果:等待... 直到事务A提交后返回 900                      │
│  -- 原理:加排他锁,强制读最新版本,阻塞等待                    │
│  COMMIT;                                                     │
└─────────────────────────────────────────────────────────────┘

总结

你的顾虑MVCC 的答案
「写的时候读,数据不准确」快照读 故意读"旧"数据,换取不阻塞
「但我需要准确数据」当前读 (FOR UPDATE),牺牲并发换准确
「到底准不准」快照读在事务开始时是一致的、自洽的,只是不是最新的

MVCC 不是万能药,而是给开发者选择权:

要并发?用快照读。要准确?用当前读。

这也是为什么 InnoDB 同时提供两种机制,而不是一刀切。

到此这篇关于mysql从零单排之快照读与当前读的实现的文章就介绍到这了,更多相关mysql 快照读与当前读内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 跳槽必备之你设计索引的原则是什么?怎么避免索引失效?

    跳槽必备之你设计索引的原则是什么?怎么避免索引失效?

    索引的设计可以遵循一些已有的原则,创建索引的时候请尽量符合这些原则,便于提升索引地使用效率,更高效地使用索引。今天给大家介绍跳槽必备之你设计索引的原则是什么?怎么避免索引失效?感兴趣的朋友一起看看吧
    2021-05-05
  • mysql 5.7更改数据库的数据存储位置的解决方法

    mysql 5.7更改数据库的数据存储位置的解决方法

    随着MySQL数据库存储的数据逐渐变大,已经将原来的存储数据的空间占满了,导致mysql已经链接不上了。所以要给存放的数据换个地方,下面小编给大家分享mysql 5.7更改数据库的数据存储位置的解决方法,一起看看吧
    2017-04-04
  • MySQL 查询某个字段含有字母数字的值示例详解

    MySQL 查询某个字段含有字母数字的值示例详解

    在本文中,我们详细介绍了如何在 MySQL 中查询某个字段含有字母和数字的值,我们首先介绍了正则表达式的基础知识,然后通过五个具体示例展示了如何应用这些知识,通过这些示例,我们可以看到正则表达式在处理复杂字符串模式匹配时的强大功能,感兴趣的朋友跟随小编一起看看吧
    2024-05-05
  • MySQL的表约束的具体使用

    MySQL的表约束的具体使用

    本文主要介绍了MySQL的表约束,通过合理地使用 NOT NULL、UNIQUE、PRIMARY KEY、FOREIGN KEY 和 CHECK 约束,可以有效防止错误数据进入数据库,感兴趣的可以了解一下
    2024-07-07
  • MySQL迁移到PostgreSQL操作指南

    MySQL迁移到PostgreSQL操作指南

    这篇文章主要介绍了MySQL迁移到PostgreSQL操作指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起来学习吧
    2023-10-10
  • MySQL5.7.14下载安装图文教程及MySQL数据库语句入门大全

    MySQL5.7.14下载安装图文教程及MySQL数据库语句入门大全

    这篇文章主要介绍了MySQL5.7.14下载安装详细教程及MySQL数据库语句入门大全的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-09-09
  • MySQL多表查询详解下

    MySQL多表查询详解下

    这篇文章主要介绍了MySQL多表查询详解下,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • MySQL存储引擎的实现要素分析

    MySQL存储引擎的实现要素分析

    这篇文章主要为大家介绍了MySQL存储引擎的实现要素分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • MySQL库表名大小写的选择

    MySQL库表名大小写的选择

    一般在数据库使用规范中,我们都会看到这么一条:库名及表名一律使用小写英文。你有没有思考过,为什么推荐使用小写呢?库表名是否应该区分大小写呢?带着这些疑问,我们一起来看下本篇文章。
    2021-06-06
  • 初始化MySQL用户(删除匿名用户)

    初始化MySQL用户(删除匿名用户)

    安装完MySQL后,系统默认会创建一个不需要密码的root用户,和一个无用户名无密码的匿名用户(Anonymous Account)。进行下面的初始化操作以合理授权,增强安全
    2016-01-01

最新评论