bigywater.tech
MySQL / InnoDB · 14

可重复读:同一个事务为什么两次读到一样的结果

开两个MySQL连接,A的一个REPEATABLE READ事务里连续两次COUNT(*),中途B插入并提交了新数据,验证A是否会看到变化。

3→3→5
A事务内两次读的结果序列
无锁
B的插入完全不受A阻塞
快照边界=事务边界
REPEATABLE READ的可见性规则

做的事:两个连接,一个事务,一次的插入

A开一个事务连续读两次,中途B插入并提交新数据。

操作时间线连接A: SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ 连接A: START TRANSACTION 连接A: SELECT COUNT(*) FROM t4 → 结果 3 连接B: INSERT INTO t4 (v) VALUES (100),(200) (B是autocommit,立刻提交) 连接B: SELECT COUNT(*) FROM t4 → 结果 5(B自己当然能看到自己刚插入的) 连接A: SELECT COUNT(*) FROM t4 → 结果?(同一个事务,第二次读)

验证:A的第二次读,结果是多少

B已经插入并提交了2行,A的事务还没提交。

结果A第一次读(B插入之前): 3 B插入并提交,B自己读: 5 A第二次读(同一事务,B已提交之后): 3 ← 跟第一次一样,没有变化! A commit() A开始新事务,读: 5 ← 提交之后,新事务才能看到

结果确认:A在同一个事务里两次读到的都是3,哪怕B已经插入并提交了新数据。只有A自己commit之后,开一个新事务重新读,才看到5。这就是REPEATABLE READ的行为——一个事务从它第一次读数据的那一刻起,就拿到了一份"快照",事务内所有后续的普通SELECT都基于这份快照,不管其他事务在这期间提交了什么。

这跟"锁"没有任何关系——A没有锁任何行,B的INSERT完全没有被阻塞,是立刻成功提交的。A看不到B的新数据,纯粹是因为MVCC给A的这次读操作分配的可见性范围(基于undo log构造的历史版本),不包含B事务提交之后的改动。

分步演示:REPEATABLE READ的快照边界

每一步都是本文上面用两个MySQL连接跑出来的原始结果。

同一个事务,两次读,拿到的是同一份快照
这一步新验证的读取

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),默认隔离级别REPEATABLE READ
    • pymysql 开两个独立连接模拟A、B两个并发会话
    京ICP备2026067502号-1京公网安备11010502063487号