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

MySQL 如何实现可重复读?

文章目录

      • MySQL 可重复读的实现原理
        • 1. 多版本并发控制(MVCC)
        • 2. 隐藏列
        • 3. 读视图(ReadView)
        • 4. 版本链
      • 案例说明
        • 案例 1:避免不可重复读
        • 案例 2:避免幻读
      • 说明


MySQL 可重复读的实现原理

1. 多版本并发控制(MVCC)

MySQL 的 InnoDB 存储引擎通过多版本并发控制(MVCC)机制实现了可重复读。MVCC 通过在数据行上存储版本信息,使得读操作可以访问到在当前事务开始时的数据快照,从而保证了事务的隔离性。

2. 隐藏列

InnoDB 在每行记录后面保存两个隐藏的列:

  • DB_TRX_ID:记录创建该行的事务的事务ID。
  • DB_ROLL_PTR:指向该行的回滚指针,用于指向该行的旧版本。
3. 读视图(ReadView)

当一个事务开始时,InnoDB 会创建一个读视图(ReadView),该视图包含了事务开始时所有已提交数据版本的信息。读视图的主要内容包括:

  • min_trx_id:在创建读视图时,系统中活跃事务的最小事务ID。
  • max_trx_id:在创建读视图时,系统中活跃事务的最大事务ID。
  • m_ids:在创建读视图时,系统中活跃事务的事务ID列表。
4. 版本链

每次对数据进行修改时,InnoDB 会生成一个新的数据版本,并将旧版本的数据通过回滚指针链接起来,形成一个版本链。读操作时,事务会根据读视图中的信息,沿着版本链查找符合读视图条件的数据版本。

案例说明

案例 1:避免不可重复读

假设有一个表 inventory,初始数据如下:

CREATE TABLE inventory (
    id INT AUTO_INCREMENT PRIMARY KEY,
    product_name VARCHAR(255) NOT NULL,
    quantity INT NOT NULL
);
INSERT INTO inventory (product_name, quantity) VALUES ('商品A', 10);
  1. 事务A 开始:

    START TRANSACTION;
    SELECT quantity FROM inventory WHERE product_name = '商品A';
    -- 查询结果:quantity = 10
    
  2. 事务B 开始并修改数据:

    START TRANSACTION;
    UPDATE inventory SET quantity = quantity - 1 WHERE product_name = '商品A';
    COMMIT;
    
  3. 事务A 再次查询:

    SELECT quantity FROM inventory WHERE product_name = '商品A';
    -- 查询结果:quantity = 10
    COMMIT;
    

在这个案例中,事务A在事务B提交修改后,仍然看到的是初始的库存数量10,这是因为事务A使用了MVCC机制,读取的是事务开始时的数据快照。

案例 2:避免幻读

假设有一个表 account,初始数据如下:

CREATE TABLE account (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    balance INT NOT NULL
);
INSERT INTO account (name, balance) VALUES ('lilei', 400), ('hanmei', 500);
  1. 事务A 开始:

    START TRANSACTION;
    SELECT * FROM account;
    -- 查询结果:lilei 400, hanmei 500
    
  2. 事务B 插入新数据并提交:

    START TRANSACTION;
    INSERT INTO account (name, balance) VALUES ('zhangsan', 600);
    COMMIT;
    
  3. 事务A 再次查询:

    SELECT * FROM account;
    -- 查询结果:lilei 400, hanmei 500
    COMMIT;
    

在这个案例中,事务A在事务B插入新数据后,仍然看到的是初始的两条记录,这是因为事务A使用了MVCC机制,读取的是事务开始时的数据快照。

说明

  • 快照读:在可重复读隔离级别下,SELECT 操作是快照读,读取的是事务开始时的数据快照,不会看到其他事务在当前事务开始后对数据的修改。
  • 当前读INSERTUPDATEDELETE 操作是当前读,会读取最新的数据版本,并且会更新版本号。
  • 幻读:虽然可重复读可以避免不可重复读,但仍然可能遇到幻读问题。幻读是指在一个事务中,多次查询满足某个条件的记录,由于其他事务插入了新记录,导致查询结果集的行数不同。InnoDB 通过间隙锁(Gap Lock)和 next-key 锁(Next-Key Lock)来部分解决幻读问题。

通过上述原理和案例,可以更好地理解 MySQL 的可重复读隔离级别是如何通过 MVCC 机制实现的。


http://www.kler.cn/a/502807.html

相关文章:

  • 【华为云开发者学堂】基于华为云 CodeArts CCE 开发微服务电商平台
  • Springboot Rabbitmq + 线程池技术控制指定数量task执行
  • vue2修改表单只提交被修改的数据的字段传给后端接口
  • WPF中如何在MVVM模式下跨线程更新UI
  • 零样本极速复刻语音!F5-TTS本地部署教程
  • ffmpeg常用命令及介绍
  • HarmonyOS应用开发者初级认证最新版– 2025/1/13号题库新版
  • 49_Lua调试
  • leetcode_1678. 设计 Goal 解析器
  • 标准Android开发jdk和gradle和gradle AGP和AndroidStudio对应版本
  • 基于Android的嵌入式车载导航系统(源码+lw+部署文档+讲解),源码可白嫖!
  • 串行总线详解 I2C(IIC)
  • 从前端视角看设计模式之创建型模式篇
  • 初识C++(二)
  • windows和linux的抓包方式
  • C# Winform:项目引入SunnyUI后,显示模糊
  • Unknown Kotlin JVM target: 21
  • 如何创建一个数组并指定初始大小?
  • MATLAB学习笔记目录
  • 高性能多链 Layer2 基础设施 Cartesi:2024 生态发展回顾
  • Three.js 用户交互:构建沉浸式3D体验的关键
  • 透明部署、旁路逻辑串联的区别
  • 【数据结构-堆】力扣1792. 最大平均通过率
  • go中协程的生命周期
  • OpenCV实现Kuwahara滤波
  • Redis优化建议详解