侧边栏壁纸
博主头像
此昵称不存在 博主等级

行动起来,活在当下

  • 累计撰写 35 篇文章
  • 累计创建 7 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

理解 MySQL 中的 MVCC 和快照读

Administrator
2025-02-10 / 0 评论 / 0 点赞 / 60 阅读 / 0 字

理解 MySQL 中的 MVCC 和快照读

在 MySQL 中,MVCC(多版本并发控制) 是用于处理高并发事务的关键机制。它通过管理数据的多个版本,允许多个事务并行运行,确保数据库的一致性和隔离性,避免加锁,提高并发能力。

什么是 MVCC

MVCC(多版本并发控制)是一个允许多个事务并行执行而不会互相干扰的机制。它通过给每个事务分配一个唯一的事务 ID,并维护数据的多个版本,确保并发事务之间的数据一致性,避免加锁,提高并发能力。

MVCC 的核心元素

  • 事务 ID:每个事务都有一个唯一的事务 ID。通过事务 ID,InnoDB 能够区分不同版本的数据。
  • 回滚段(Undo Logs):每个操作(如插入、更新、删除)都会在回滚段中记录一份旧的数据版本。当需要恢复到某个时间点时,InnoDB 会使用这些回滚日志。
  • 可见性:事务在读取数据时,会根据事务 ID 和回滚段来判断数据的版本,确保查询的数据是事务开始时的快照。

快照读(Consistent Read)实现

快照读是 MVCC 的核心,指的是在查询时提供一致的视图,即使其他事务在此期间进行了更新。具体实现如下:

  1. 事务开始时创建一个快照:InnoDB 会为每个事务创建一个数据快照,保存该时刻的数据版本。
  2. 查询时使用快照数据:当事务执行查询时,InnoDB 会返回符合事务开始时快照的数据,而不是最新的数据。
  3. 通过 Undo Logs 实现一致性:InnoDB 会通过扫描回滚段(Undo Logs)来获取历史版本的数据,确保快照数据的一致性。

快照读的工作原理

  • 在查询时,InnoDB 会根据事务 ID 和回滚段中的数据版本,确定哪些数据是可见的。
  • 事务会看到从其开始时的快照数据,而其他事务对数据的插入、更新操作不会影响当前事务的查询结果。

快照读示例:

  1. 创建示例表
    首先,我们创建一个 users 表,并插入一些初始数据:
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    balance DECIMAL(10,2)
) ENGINE=InnoDB;
INSERT INTO users (name, balance) VALUES ('Alice', 100.00), ('Bob', 200.00);
  1. 开启两个事务,模拟 MVCC 读取数据
    事务 A(读取 Alice 的余额)
BEGIN;
SELECT * FROM users WHERE name = 'Alice'; -- 快照读

此时,Alice 的 balance 是 100.00(读取的是旧版本快照)。

事务 B(更新 Alice 的余额)

BEGIN;
UPDATE users SET balance = 150.00 WHERE name = 'Alice';
COMMIT;

此时,Alice 的 balance 更新为 150.00,但 事务 A 仍然看到 100.00,因为它使用的是 事务启动时的快照。

事务 A 再次读取 Alice 的余额
SELECT * FROM users WHERE name = 'Alice'; -- 仍然是快照读
返回结果仍然是 100.00,说明 事务 A 看到的仍是旧版本数据(MVCC 机制生效)。

事务 A 提交后再查询

COMMIT;
SELECT * FROM users WHERE name = 'Alice'; -- 现在读到的是最新数据

此时,Alice 的 balance 才变成 150.00。

当前读

接上文的例子,如果需要在事务中读取实时数据,而不是快照读,可在select语句后加上行锁for update 或者共享锁lock in share mode

BEGIN;
SELECT * FROM users WHERE name = 'Alice' lock in share mode; -- 当前读
SELECT * FROM users WHERE name = 'Alice' for update; -- 当前读

此时,Alice 的 balance 才变成 150.00。
tips:由于是当前读,则MySQL会自动帮我们加上间隙锁。

间隙锁

间隙锁是为了解决的在可重复读事务模式下的幻读问题。在使用当前读(如 SELECT FOR UPDATE SELECT LOCK IN SHARE MODE)时,InnoDB 会加锁以确保数据一致性,并可能触发 间隙锁。间隙锁用于防止其他事务在当前事务所锁定的数据范围内插入数据,从而避免幻读现象。

  1. 当前读触发间隙锁
    当使用 SELECT FOR UPDATESELECT LOCK IN SHARE MODE 时,InnoDB 会锁定符合条件的记录,并锁定相关的 间隙,防止其他事务插入符合查询条件的数据。
BEGIN;
SELECT * FROM users WHERE age > 20 FOR UPDATE;

此时其他事务无法再插入age大于20的记录,会阻塞等待,直到上述事务提交
2. 一致性读不触发间隙锁
普通的 SELECT 查询不会触发间隙锁,因为它依赖 MVCC 来提供数据一致性,而不会加锁或防止其他事务插入数据。

SELECT * FROM users WHERE age > 20;
tips:间隙锁(Gap Lock)是 左闭右开 的,即它锁定的范围包括区间的左边界,但不包括右边界。左闭右开([a, b))意味着间隙锁会锁住一个区间的左边界(a),但不锁住右边界(b)。例如,如果有一个条件是 age BETWEEN 20 AND 30,间隙锁会锁定从 age = 20age = 30 之间的区间,但不会包括 age = 30 的数据。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区