1.MVCC功能

MVCC(多版本并发控制)是 MySQL InnoDB 实现高并发读写的核心机制,它通过保存数据的多个版本来实现读写不阻塞,提高了数据库的并发性能。

2 MVCC核心思想

"MVCC 的核心思想是:为每个事务创建一个数据快照,不同事务看到的数据版本可能不同。这样就实现了:

  • 读不阻塞写:读取历史版本

  • 写不阻塞读:修改创建新版本

  • 避免了大量的锁竞争"

2. 关键组成部分

"MVCC 主要依赖于三个关键部分:

① 隐藏字段:

  • DB_TRX_ID:最近修改的事务ID

  • DB_ROLL_PTR:指向 undo log 的回滚指针

  • DELETE_BIT:删除标记

② ReadView(读视图):
相当于事务的快照,包含:

  • m_ids:当前活跃事务ID列表

  • min_trx_id:最小活跃事务ID

  • max_trx_id:最大分配事务ID+1

③ Undo Log(回滚日志):
存储数据的历史版本,形成版本链"

3. 工作流程

"以 SELECT 查询为例:

    1. 首先获取当前事务的 ReadView

    2. 读取数据行的最新版本

    3. 检查该版本的 DB_TRX_ID

      • 如果小于 min_trx_id:可见(事务已提交)

      • 如果大于等于 max_trx_id:不可见(事务后开始)

      • 如果在 m_ids 中:不可见(事务进行中)

      • 否则:可见

    4. 如果不可见,通过 DB_ROLL_PTR 找到 undo log 中的上一个版本,重复判断"

4. 不同隔离级别的差异

"不同隔离级别下 MVCC 的表现不同:

  • RC(读已提交):每次 SELECT 都生成新 ReadView

  • RR(可重复读):事务第一次 SELECT 时生成 ReadView,后续复用

  • RU(读未提交):直接读最新数据,不用 MVCC

  • Serializable(串行化):退化为加锁读写,不用 MVCC"

5、实际事务场景演示(隔离级别:可重复读)

 
假设系统初始事务 ID 从 100 开始分配,执行以下 3 个事务:

事务 1(读事务,TRX_ID=100)

sql
BEGIN; -- 事务启动,分配 TRX_ID=100
-- 第一次查询 user 表
SELECT * FROM user WHERE id=1; -- 结果:name=Alice, age=20
  • 生成 Read View1:此时无其他活跃事务,m_ids=[]min_trx_id=100max_trx_id=101
  • 版本判断:行数据的 DB_TRX_ID 为插入时的事务 ID(假设为 99),满足 99 < 100,可见,返回结果。

事务 2(写事务,TRX_ID=101)

在事务 1 未提交时,执行事务 2:
sql
BEGIN; -- 分配 TRX_ID=101
UPDATE user SET age=22 WHERE id=1; -- 修改 id=1 的数据
-- 此时事务 2 未提交
  • 版本链变化:更新操作会将旧版本(age=20,DB_TRX_ID=99)写入 undo log,新版本(age=22,DB_TRX_ID=101)留在数据页,DB_ROLL_PTR 指向 undo log 中的旧版本。
  • 事务 2 未提交,因此新版本的 DB_TRX_ID=101 属于活跃事务,对其他事务不可见。

事务 1 再次查询(同一事务内) 

回到事务 1,执行第二次查询:
sql
-- 事务 1 未提交,再次查询同一数据
SELECT * FROM user WHERE id=1; -- 结果仍为:name=Alice, age=20
  • 关键特性:可重复读隔离级别下,事务内仅在第一次查询时生成一次 Read View(Read View1 复用)。如果是读已提交就在每次select是生成新的readview
  • 版本判断:新版本的 DB_TRX_ID=101 满足 100 ≤ 101 < 101 不成立,且 101 在当前活跃事务 ID 集合中,因此不可见;转而通过 DB_ROLL_PTR 读取 undo log 中的旧版本(age=20),保证了可重复读。

事务 2 提交,事务 1 第三次查询

sql
-- 先提交事务 2
COMMIT; -- 事务 2 提交,TRX_ID=101 变为非活跃

-- 事务 1 仍未提交,第三次查询
SELECT * FROM user WHERE id=1; -- 结果还是:name=Alice, age=20
  • 原因:可重复读隔离级别下 Read View 复用,依旧用 Read View1 判断版本。新版本 DB_TRX_ID=101 满足 100 ≤ 101 < 101 不成立,仍不可见,继续读取旧版本。

事务 1 提交后,新事务查询

sql
-- 提交事务 1
COMMIT;

-- 开启新事务 3(TRX_ID=102)
BEGIN;
SELECT * FROM user WHERE id=1; -- 结果:name=Alice, age=22
  • 生成 Read View2:此时无活跃事务,m_ids=[]min_trx_id=102max_trx_id=103
  • 版本判断:新版本 DB_TRX_ID=101 < 102,满足可见性规则,返回最新数据。

 

posted on 2026-01-15 07:11  colorfulworld  阅读(4)  评论(0)    收藏  举报