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 快照读与当前读内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 磁盘已满造成的mysql启动失败问题分享

    磁盘已满造成的mysql启动失败问题分享

    这篇文章主要介绍了磁盘已满造成的mysql启动失败问题分享,需要的朋友可以参考下
    2014-04-04
  • Workbench连接不上阿里云服务器Ubuntu的Mysql解决方法(已测)

    Workbench连接不上阿里云服务器Ubuntu的Mysql解决方法(已测)

    这两天为了解决workbench连接不上阿里云服务器的问题,搞得头大,网上搜到的教程都大同小异,但唯独到我这就是行不通。不过好在最后终于解决了,记录一下这个坑爹的过程,另外脚本之家小编特把这些问题整理了一下,看完这一篇文章基本上就解决了
    2020-02-02
  • MySQL快速对比数据技巧

    MySQL快速对比数据技巧

    这篇文章主要介绍了MySQL快速对比数据的方法以及技巧分享,如果对此有兴趣,一起跟着小编学习下吧。
    2018-02-02
  • mysql5.7.17 zip 解压安装详细过程

    mysql5.7.17 zip 解压安装详细过程

    这篇文章主要为大家详细介绍了mysql5.7.17 zip 解压安装详细过程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02
  • Mysql更改默认引擎为Innodb的步骤方法

    Mysql更改默认引擎为Innodb的步骤方法

    mysql默认是关闭InnoDB存储引擎的使用的,而Mysql默认引擎是MyISAM,而MyISAM并不支持事务处理,因为最近项目中的需要所以要更换引擎,通过查找网上的资料后解决了,现在将步骤方法分享给大家,有需要的朋友们可以参考借鉴,下面来一起看看吧。
    2016-12-12
  • 解决数据库有数据但查询出来的值为Null问题

    解决数据库有数据但查询出来的值为Null问题

    这篇文章主要介绍了解决数据库有数据但查询出来的值为Null问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • MySQL数据备份之mysqldump的使用方法

    MySQL数据备份之mysqldump的使用方法

    mysqldump常用于MySQL数据库逻辑备份,这篇文章主要给大家介绍了关于MySQL数据备份之mysqldump使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2021-11-11
  • mysql触发器实现oracle物化视图示例代码

    mysql触发器实现oracle物化视图示例代码

    mysql触发器实现oracle物化视图即不是基于基表的虚表,而是根据表实际存在的实表,需要的朋友可以参考下
    2014-02-02
  • MySQL数据权限的实现详情

    MySQL数据权限的实现详情

    这篇文章主要介绍了MySQL数据权限的实现详情,文章通过实际案例,从代码实战的角度来实现这样的一个数据权限。具体详细介绍,具有一定的参考价值
    2022-08-08
  • MySQL对相同字段创建不同索引解析

    MySQL对相同字段创建不同索引解析

    这篇文章主要为大家介绍了MySQL 对相同字段创建不同索引解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11

最新评论