当前读和快照读

在 MySQL 的 InnoDB 存储引擎中,存在两种不同的读取模式:当前读(Current Read)快照读(Snapshot Read)。这两者在事务并发控制中扮演着不同的角色,并通过不同的机制来确保数据一致性。下面解释它们的区别及应用场景。

1. 当前读(Current Read)

定义: 当前读指的是读取数据的最新版本,无论其他事务是否对这些数据做了修改,只要事务已经提交,当前读都能看到这些修改。当前读通常会加锁,确保在读取和修改期间数据的一致性。

适用场景:
当前读通常用于需要修改数据的操作(INSERTUPDATEDELETE),因为这些操作需要确保读取的最新数据是其他事务不可修改的。以下操作都是当前读:

  • SELECT ... FOR UPDATE (加排它锁)
  • SELECT ... LOCK IN SHARE MODE (加共享锁)
  • UPDATE
  • DELETE
  • INSERT

工作原理:
在执行当前读操作时,事务会对所读取的行加锁,防止其他事务修改这些行。例如,在执行 SELECT ... FOR UPDATE 时,事务会对查询结果中的每一行加上排它锁,其他事务在该行上的更新或删除操作将被阻塞,直到当前事务提交或回滚。

例子: 假设有两个事务 AB

  • 事务 A 执行 SELECT ... FOR UPDATE 读取了一些行,并对这些行加锁。
  • 事务 B 想要更新或插入这些行,会被阻塞,直到事务 A 完成。
  • 事务 A 能看到其他事务已经提交的修改。

2. 快照读(Snapshot Read)

定义: 快照读是通过多版本并发控制(MVCC)来实现的,它读取的是事务启动时的历史快照数据,而不是最新的数据。这意味着在快照读的过程中,即使有其他事务提交了新的更改,快照读仍然只能看到快照中的旧数据,不会受到新提交的事务的影响。

适用场景:
快照读通常用于只需要读取数据,而不需要加锁的查询操作。常见的快照读操作包括:

  • 普通的 SELECT 语句(不加锁的查询)

工作原理:
InnoDB 会维护一个事务快照(事务开始时的数据库版本),快照读通过 MVCC 机制来访问这个快照中的数据版本,而不是实时的最新版本。因此,快照读不会加锁,不会阻塞其他事务,也不会被其他事务的更新所影响。

例子: 假设有两个事务 AB

  • 事务 A 开始时读取了一份快照,随后事务 B 对某行进行了 UPDATE 操作并提交。
  • 事务 A 即使再次执行 SELECT,也只能看到快照中的旧数据,而不会看到事务 B 提交的新数据。

3. 区别

特性 当前读(Current Read) 快照读(Snapshot Read)
数据版本 读取最新的数据版本 读取事务开始时的快照
是否加锁 是,加锁以保证数据一致性 否,不加锁
是否能看到其他事务的提交 能看到其他事务提交的最新数据 只能看到事务开始时的快照数据,不会看到其他事务的提交
使用场景 UPDATEDELETESELECT ... FOR UPDATE 普通的 SELECT 查询

4. 总结

  • 当前读用于需要修改数据或加锁的操作,它确保读写一致性,读取最新数据,且通过锁机制避免并发冲突。
  • 快照读用于只读的查询操作,它通过 MVCC 保证读到的始终是事务启动时的快照,避免幻读、不可重复读等问题,同时无需加锁,因此并发性能较好。