不可重复读复现与RR解决
实验一:脏读复现 → RC 解决
实验目的:亲眼看到一个事务读到另一个事务还没提交的修改。
1.1 脏读复现(READ UNCOMMITTED)
时间轴:
T1 Session A: BEGIN; UPDATE... SET age=999(未提交)
T2 Session B: BEGIN; SELECT... → 读到 age=999 ← 这就是脏读!
T3 Session A: ROLLBACK(不承认!age 变回 25)
T4 Session B: SELECT... → 看到 age=25
结果:Session B 中间读到了一个"不存在"的数据
-- ========== 两个 Session 都执行 ==========
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- ========== Session A ==========
BEGIN;
-- 把张三年龄改成 999,但不提交
UPDATE users SET age = 999 WHERE id = 1;
-- Query OK, 1 row affected
-- 先不 COMMIT,切换到 Session B
-- ========== Session B ==========
BEGIN;
-- 查询张三的年龄
SELECT id, name, age FROM users WHERE id = 1;
-- +----+------+-----+
-- | id | name | age |
-- +----+------+-----+
-- | 1 | 张三 | 999 | ← 读到了!但 A 还没提交!
-- +----+------+-----+
-- 这就是脏读——读到了一个可能被撤销的修改
-- 现在切回 Session A
-- ========== Session A ==========
ROLLBACK; -- 反悔了!不改了!
-- 切回 Session B
-- ========== Session B ==========
SELECT id, name, age FROM users WHERE id = 1;
-- +----+------+-----+
-- | id | name | age |
-- +----+------+-----+
-- | 1 | 张三 | 25 | ← 又变回 25 了!
-- +----+------+-----+
-- Session B 第一次读到的 999 是"假数据"——A 根本没提交
ROLLBACK;
graph TD Result["📋 实验结果:Session B 在 READ UNCOMMITTED 下<br/><br/>第一次 SELECT → age = 999(脏数据!)<br/>第二次 SELECT → age = 25(A 回滚了)<br/><br/>结论:READ UNCOMMITTED 能读到别人未提交的修改<br/>→ 别人一回滚,你基于 999 做的业务逻辑全部报废"] style Result fill:#fff3cd,stroke:#ffc107,stroke-width:2px
1.2 RC 解决脏读
-- ========== 两个 Session 都执行 ==========
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- ========== Session A ==========
BEGIN;
UPDATE users SET age = 888 WHERE id = 1;
-- 不提交
-- ========== Session B ==========
BEGIN;
SELECT id, name, age FROM users WHERE id = 1;
-- +----+------+-----+
-- | id | name | age |
-- +----+------+-----+
-- | 1 | 张三 | 25 | ← 还是 25!读不到 888!
-- +----+------+-----+
-- 切回 A 提交
-- ========== Session A ==========
COMMIT; -- 确定修改
-- ========== Session B ==========
SELECT id, name, age FROM users WHERE id = 1;
-- +----+------+-----+
-- | id | name | age |
-- +----+------+-----+
-- | 1 | 张三 | 888 | ← A 提交后,B 才能看到
-- +----+------+-----+
-- 但注意:B 同一事务内两次读到了不同的值(25 → 888)
-- 这就是"不可重复读"——RC 防住了脏读,但引入了不可重复读
ROLLBACK;
-- Session A 也 ROLLBACK 恢复数据
ROLLBACK;
graph TD Result["📋 实验结果:READ COMMITTED 下<br/><br/>✅ Session B 读不到 A 未提交的值(防脏读)<br/>❌ 但 A 提交后 B 再读 → 值变了(不可重复读!)<br/><br/>RC = 只能读已提交,但每次 SELECT 都可能看到不同结果"] style Result fill:#d4edda,stroke:#28a745,stroke-width:2px
速记卡(面试闪卡)
Q1:一句话讲清「不可重复读复现与RR解决」到底是什么?
A:本笔记通过实验复现事务隔离问题:脏读由 RC 解决,不可重复读由 RR(可重复读)解决。
Q2:脏读复现:READ UNCOMMITTED 读到未提交 —— 怎么理解?
A:像看到别人写到一半的草稿:A 改了 age=999 没提交,B 在 RU 级别就读到了,A 一回滚 B 全乱——这就是脏读(Dirty Read)。
Q3:RC 如何防住脏读 —— 怎么理解?
A:像只认盖章的正式文件:READ COMMITTED 只让 B 读到 A 已提交的值,没提交的 999 看不见,脏读被挡住(Read Committed)。
Q4:RC 引入的不可重复读 —— 怎么理解?
A:像同一次翻账本前后不一:B 同一事务里先读 25、A 提交 888 后 B 再读变 888——RC 防住脏读却放进了不可重复读(Non-repeatable Read)。
Q5:RR(可重复读)如何解决不可重复读 —— 怎么理解?
A:像给账本拍快照:REPEATABLE READ 让 B 整个事务看到同一份数据,A 提交的新值不影响 B 的两次读取,前后一致(Repeatable Read)。
Q6:核心速记主线有哪些?
-
脏读:读到别人未提交的数据,RU 级别才出现
-
RC:只读了已提交的,防住脏读
-
不可重复读:同事务两次读同行结果不同,RC 仍有
-
RR:事务内数据快照一致,解决不可重复读
口诀
A:脏读读到未提交,RU 级别才现身
RC 只认已提交,脏读从此被关门
可重复读 RC 留,同事务里两结果
RR 拍快照,前后一致不乱走