快照读与当前读

总结

介绍对比快照读与当前读,以及 事务隔离级别 对快照读的影响。

简介

在 MySQL(尤其是使用 InnoDB 存储引擎)中,快照读(Snapshot Read)和当前读(Current Read)是两种不同的读取方式,它们在事务隔离级别下对数据可见性有不同的处理方式,特别是在使用多版本并发控制(MVCC)时表现明显。

详情

快照读(Snapshot Read)

定义:

快照读是指在事务中读取一个 一致性读视图(Consistent Read View)下的数据快照,而不是当前数据库中的最新数据。这种读取方式通常用于普通的 SELECT 语句。

示例:

SELECT * FROM users WHERE id = 1;

特点:

  • 使用的是 MVCC(多版本并发控制) 机制,读取的是数据的历史版本。
  • 不加锁(除非显式加锁),不会阻塞其他事务的写操作。
  • 在可重复读(REPEATABLE READ)隔离级别下,一个事务在整个生命周期中看到的是事务开始时的数据快照。
  • 在读已提交(READ COMMITTED)隔离级别下,每次查询会生成一个新的一致性视图。

适用场景:

适用于不需要获取最新数据的场景,例如报表查询、数据分析等。

当前读(Current Read)

定义:

当前读是指读取数据的最新版本,并通常会对读取的数据加锁,以防止其他事务修改。这种读取方式会触发锁机制,确保读到的是最新的数据,并防止并发写入。

示例:

以下 SQL 语句都会触发当前读:

SELECT * FROM users WHERE id = 1 FOR UPDATE;
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE;
UPDATE users SET name = 'Tom' WHERE id = 1;
DELETE FROM users WHERE id = 1;
INSERT INTO users (name) VALUES ('Jerry');

特点:

  • 读取的是最新的数据版本。
  • 会加锁(排他锁或共享锁),可能阻塞其他事务的读写。
  • 不依赖 MVCC 的一致性视图,而是直接读取当前最新数据。
  • 保证读取的数据在事务提交前不会被其他事务修改。

适用场景:

适用于需要保证数据最新性、并且可能需要进行修改的场景,例如事务中的写操作、转账、库存扣减等。

表格对比

类型是否加锁读取版本是否触发 MVCC是否阻塞其他事务隔离级别影响典型语句场景
快照读历史版本 (一致性读视图)明显(尤其在 RR 级别)SELECT查询、报表、分析
当前读是(排他锁或共享锁)最新版本不明显(总是读最新)UPDATE, DELETE, INSERT修改、写操作、并发控制

事务隔离级别对快照读的影响

  • READ UNCOMMITTED:不使用快照读,直接读未提交的数据。
  • READ COMMITTED(RC):每次快照读生成新的一致性视图。
  • REPEATABLE READ(RR):整个事务期间使用同一个一致性视图。
  • SERIALIZABLE:所有读操作都变成当前读(隐式加锁)。

举例说明

假设有一个表 users(id, name),初始数据为 (1, 'Alice')

时间点事务 A事务 B
T0START TRANSACTION;
T1SELECT * FROM users WHERE id = 1;
T2START TRANSACTION;
T3UPDATE users SET name = 'Bob' WHERE id = 1;
T4COMMIT;
T5SELECT * FROM users WHERE id = 1;
T6SELECT * FROM users WHERE id = 1 FOR UPDATE;
T7COMMIT;
隔离级别T1T3T5(快照读)T6(当前读)说明
RUAlice可能读到 Bob(脏读)BobBob快照读不生效,直接读最新数据,即使事务 B 尚未提交也能读到修改
RCAliceAliceBob(生成新一致性视图)Bob每次快照读生成新的一致性视图,事务 B 提交后能读到新数据
RRAliceAliceAlice(保持初始一致性视图)Bob快照读在整个事务中保持一致性视图;当前读读最新数据
SERIALIZABLEAlice(隐式加 S 锁)AliceAliceAlice所有普通 SELECT 都加锁,事务 B 的 UPDATE 被阻塞直到事务 A 提交

关联文章