“不可重复读”和“可重复读”的底层含义就是一句话:
“同一事务里先后两条 SELECT 生成的 ReadView 是不是同一个?”
1. 可重复读(RR)
事务里第一次 SELECT 时生成 ReadView,整个事务不再重新生成。
后一条 SELECT 仍然用同一张快照,所以只要别人在那之后提交,你也看不见 → 多次读结果一样 → “可重复读”。
2. 不可重复读(RC)
每条 SELECT 开始都重新生成 ReadView,快照点永远取“语句开始那一刻最新已提交版本”。
如果别的事务在这两条 SELECT 之间提交,第二次就能看到新数据 → 两次结果不一样 → “不可重复读”。
底层只有这一个差别:
ReadView 的生命周期 = 事务级(RR) vs 语句级(RC);
上层现象就是“能不能重复读到相同行”。
在 MySQL(InnoDB)里,“当前读” 与 “快照读” 是两种读取数据时所使用的版本/加锁策略,直接决定了:
你能不能读到别的事务刚提交的新数据
你读的时候要不要加锁、会不锁别人
下面按“定义 → 实现机制 → 语句表现 → 可见性 → 锁冲突 → 适用场景”六层给你拆开。
1. 定义一句话
快照读(consistent read):读取事务启动时那一刻已经存在的快照,不加锁、不阻塞、不冲突, repeatable-read 级别下保证“可重复读”。
当前读(locking read):读取最新已提交版本,并对返回记录加锁(S/X),阻塞其他事务的并发写,保证“读到的就是此刻最新且别人改不了”。
2. 实现机制
快照读:利用 InnoDB MVCC(隐藏列 trx_id + ReadView),只在第一次 SELECT 时生成 ReadView,之后复用,读到的是“历史版本”。
当前读:直接走最新版本链,同时加记录锁 / 间隙锁(lock_mode=X or S),写入前必须获得锁,因而也叫“锁定读”。
3. SQL 语句表现
4. 可见性差异
会话 1(RR 级别)
BEGIN;
SELECT money FROM account WHERE id=1; -- 100 快照读
-- 此时会话 2 把 money 改成 200 并提交
SELECT money FROM account WHERE id=1; -- 100 快照读,依旧 100
SELECT money FROM account WHERE id=1 FOR UPDATE; -- 200 当前读,读到最新
5. 锁冲突演示
6. 典型场景选择
快照读:报表、只读查询、可重复读业务逻辑,不想被阻塞也不想阻塞别人。
当前读:
– 先读后写(“读-改-写”)必须保证读最新,否则会出现“丢失更新”;
– 需要显式加锁让后续操作排他;
– 串行化隔离级别下所有 SELECT 也退化为当前读。
一句话总结
快照读 = 看历史照片,不加锁;
当前读 = 看现场直播,加锁防改。
在 RR 级别下,普通 SELECT 用快照,SELECT … FOR UPDATE/UPDATE/DELETE 用当前读,两者结合才同时实现“高性能+一致性”。