在同一个事务里对同一个范围数两次行数,中途另一个会话插入一行新数据,看普通读和当前读对这行'幻影'的真实态度有什么不同。
session A(REPEATABLE READ)START TRANSACTION; SELECT COUNT(*) FROM accounts WHERE id BETWEEN 10 AND 90; -- 第一次 SELECT SLEEP(3); SELECT COUNT(*) FROM accounts WHERE id BETWEEN 10 AND 90; -- 第二次(B已插入并提交) SELECT COUNT(*) FROM accounts WHERE id BETWEEN 10 AND 90 FOR UPDATE; -- 第三次,当前读 COMMIT; -- session B(在A的SLEEP期间执行) INSERT INTO accounts VALUES (85, 1000, 85);
第一次普通COUNT: 8 (B执行 INSERT id=85 并提交) 第二次普通COUNT(同一事务内): 8 ← 真实没变,B插入的新行不在快照里 第三次 FOR UPDATE(当前读): 9 ← 真实变了,看见了B插入的这一行
两次普通COUNT(*)真实完全一样,这正是REPEATABLE READ隔离级别对幻读的真实防护——普通SELECT基于事务开始时的快照,B在这之后插入的新行,不管有没有提交,对A的一致性读来说都真实"不存在"。换成FOR UPDATE做当前读之后,真实立刻数出了9条,B插入的那一行(id=85)变得可见了——当前读没有快照保护,读到的是执行这条语句那一刻真实已经提交的最新数据,包括别的事务刚插入的新行。
每一步都是本文上面用两个真实并发会话跑出来的原始结果。
SELECT COUNT(*)(一致性读) vs SELECT ... FOR UPDATE(当前读)