技术大道上开拓前进

MVCC可重复读事项

Posted on By Holy place

MVCC可重复读

一、MVCC 实现可重复读的关键机制

  1. 隐藏字段与 Undo 日志
    1. 每个 InnoDB 行记录包含两个隐藏字段:
    2. DB_TRX_ID:记录最后一次修改该行的事务 ID。
    3. DB_ROLL_PTR:指向 Undo 日志中旧版本数据的指针。
    4. Undo 日志:保存数据的历史版本,用于构建事务可见的旧数据快照。
  2. Read View(读视图)
    1. 事务首次执行 SELECT 时会生成一个 Read View,包含以下信息:
      1. 活跃事务列表(trx_ids):当前未提交的事务 ID 集合。
      2. 最小活跃事务 ID(min_trx_id):活跃事务列表中的最小 ID。
      3. 最大事务 ID(max_trx_id):下一个即将分配的事务 ID。
      4. 创建者事务 ID(creator_trx_id):当前事务自身的 ID。
    2. 通过 Read View 判断数据版本的可见性规则:
      1. 如果行记录的 DB_TRX_ID < min_trx_id:说明该版本在事务开始前已提交,可见。
      2. 如果 DB_TRX_ID >= max_trx_id:说明该版本在事务开始后生成,不可见。
      3. 如果 min_trx_id ≤ DB_TRX_ID < max_trx_id:
        1. DB_TRX_ID 在 trx_ids 列表中:说明该版本由未提交事务生成,不可见。
        2. DB_TRX_ID 不在 trx_ids 列表中:说明该版本已提交,可见。
  3. 快照读(Snapshot Read) 在可重复读级别下,事务的 SELECT 操作基于第一次生成的 Read View,后续读取始终使用同一快照,确保多次读取的数据版本一致。 —

二、数据读写顺序的处理

  1. 读操作(SELECT)

    1. 流程:
      1. 访问数据页中的最新行记录。
      2. 检查 DB_TRX_ID 是否符合 Read View 的可见性规则。
      3. 若不可见,通过 DB_ROLL_PTR 找到 Undo 日志中的旧版本,逐级回溯直到找到可见版本。
    2. 示例
      -- 事务 A 第一次读取(生成 Read View)
      SELECT * FROM table WHERE id = 1;
            
      -- 事务 B 修改并提交数据
      UPDATE table SET value = 2 WHERE id = 1;
            
      -- 事务 A 再次读取(仍看到旧值)
      SELECT * FROM table WHERE id = 1; -- 结果与第一次一致
      
  2. 写操作(UPDATE/INSERT/DELETE)
    1. 流程:
      1. 当前读(Current Read):写操作直接读取最新提交的数据版本(而非快照),并加锁(如行锁、间隙锁)。
      2. 修改数据时生成新版本,更新 DB_TRX_ID 和 DB_ROLL_PTR,旧版本存入 Undo 日志。
    2. 示例: ```sql

      – 事务 A 更新数据(触发当前读) UPDATE table SET value = 3 WHERE id = 1; – 会读取事务 B 提交的最新值 2,修改为 3

    ```

  3. 事务提交与清理
    1. 提交时,InnoDB 将修改持久化到磁盘。
    2. Undo 日志中的旧版本数据在无活跃事务依赖时,由后台 Purge 线程清理。

三、可重复读如何避免幻读?

  1. 快照读:通过 Read View 保证事务内读取的数据版本固定,避免看到其他事务新增/删除的数据(幻读)。
  2. 当前读 + 间隙锁:
    1. 执行 UPDATE、DELETE 或 SELECT … FOR UPDATE 时,InnoDB 对扫描的范围加间隙锁,阻止其他事务插入新数据,从而避免幻读。

四、与读已提交(READ COMMITTED)的区别

一、隔离级别总览

| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 锁机制 | 实现原理 | |————————-|——|————|——|———————————————————————–|————————————————————————–| | READ UNCOMMITTED | ✔️ | ✔️ | ✔️ | 无锁控制(仅行锁) | 直接读取最新数据版本 | | READ COMMITTED | ✖️ | ✔️ | ✔️ | 行锁 + 短间隙锁 | MVCC + 每次 SELECT 生成新快照 | | REPEATABLE READ | ✖️ | ✖️ | △️ | 行锁 + 间隙锁(Gap Lock) + 临键锁(Next-Key Lock) | MVCC + 事务首次 SELECT 生成快照 + 间隙锁防插入 | | SERIALIZABLE | ✖️ | ✖️ | ✖️ | 范围锁(所有操作串行化) | 所有 SELECT 隐式转换为 SELECT ... FOR UPDATE |

△️ 表示:快照读可避免幻读,当前读可能触发(需显式加锁)


1. 脏读(Dirty Read)

总结

  1. MVCC 核心:通过 Undo 日志保存多版本数据,结合 Read View 实现事务隔离。
  2. 可重复读:事务内使用固定快照,确保多次读取一致性;写操作通过当前读和锁机制保证数据正确性。
  3. 适用场景:需要高度数据一致性的业务(如财务系统),但对并发性能有一定影响。

喜欢文章请关注我

程序领域
点击关注+转发,私信发送【面试】或者【资料】可以收获更多资源

公众号