当前读和快照读
在 MySQL 的 InnoDB 存储引擎中,存在两种不同的读取模式:当前读(Current Read) 和 快照读(Snapshot Read)。这两者在事务并发控制中扮演着不同的角色,并通过不同的机制来确保数据一致性。下面解释它们的区别及应用场景。
1. 当前读(Current Read)
定义: 当前读指的是读取数据的最新版本,无论其他事务是否对这些数据做了修改,只要事务已经提交,当前读都能看到这些修改。当前读通常会加锁,确保在读取和修改期间数据的一致性。
适用场景:
当前读通常用于需要修改数据的操作(INSERT、UPDATE、DELETE),因为这些操作需要确保读取的最新数据是其他事务不可修改的。以下操作都是当前读:
SELECT ... FOR UPDATE(加排它锁)SELECT ... LOCK IN SHARE MODE(加共享锁)UPDATEDELETEINSERT
工作原理:
在执行当前读操作时,事务会对所读取的行加锁,防止其他事务修改这些行。例如,在执行 SELECT ... FOR UPDATE 时,事务会对查询结果中的每一行加上排它锁,其他事务在该行上的更新或删除操作将被阻塞,直到当前事务提交或回滚。
例子:
假设有两个事务 A 和 B:
- 事务
A执行SELECT ... FOR UPDATE读取了一些行,并对这些行加锁。 - 事务
B想要更新或插入这些行,会被阻塞,直到事务A完成。 - 事务
A能看到其他事务已经提交的修改。
2. 快照读(Snapshot Read)
定义: 快照读是通过多版本并发控制(MVCC)来实现的,它读取的是事务启动时的历史快照数据,而不是最新的数据。这意味着在快照读的过程中,即使有其他事务提交了新的更改,快照读仍然只能看到快照中的旧数据,不会受到新提交的事务的影响。
适用场景:
快照读通常用于只需要读取数据,而不需要加锁的查询操作。常见的快照读操作包括:
- 普通的
SELECT语句(不加锁的查询)
工作原理:
InnoDB 会维护一个事务快照(事务开始时的数据库版本),快照读通过 MVCC 机制来访问这个快照中的数据版本,而不是实时的最新版本。因此,快照读不会加锁,不会阻塞其他事务,也不会被其他事务的更新所影响。
例子:
假设有两个事务 A 和 B:
- 事务
A开始时读取了一份快照,随后事务B对某行进行了UPDATE操作并提交。 - 事务
A即使再次执行SELECT,也只能看到快照中的旧数据,而不会看到事务B提交的新数据。
3. 区别
| 特性 | 当前读(Current Read) | 快照读(Snapshot Read) |
|---|---|---|
| 数据版本 | 读取最新的数据版本 | 读取事务开始时的快照 |
| 是否加锁 | 是,加锁以保证数据一致性 | 否,不加锁 |
| 是否能看到其他事务的提交 | 能看到其他事务提交的最新数据 | 只能看到事务开始时的快照数据,不会看到其他事务的提交 |
| 使用场景 | UPDATE、DELETE、SELECT ... FOR UPDATE |
普通的 SELECT 查询 |
4. 总结
- 当前读用于需要修改数据或加锁的操作,它确保读写一致性,读取最新数据,且通过锁机制避免并发冲突。
- 快照读用于只读的查询操作,它通过 MVCC 保证读到的始终是事务启动时的快照,避免幻读、不可重复读等问题,同时无需加锁,因此并发性能较好。