3.8.3 并发问题
本课核心知识点整理
三类并发异常的共同点
并发异常都来自事务交错执行,并且至少有两个事务访问同一数据项。区别在于冲突发生在哪个环节:写覆盖写、两次读之间被改,还是读到了未提交数据。
| 并发问题 | 核心特征 | 一句话识别 |
|---|---|---|
| 丢失修改/丢失更新 | 两个事务都修改同一数据,后写覆盖前写 | 写写覆盖 |
| 不可重复读 | 同一事务两次读同一数据,中间被别人修改 | 两次读不一样 |
| 读脏数据 | 读到别人未提交、后来可能回滚的数据 | 读到无效中间值 |
丢失修改:后写覆盖前写
设数据 A=10。两个事务都先读到 10:
| 步骤 | T1 | T2 | A 的结果 |
|---|---|---|---|
| 1 | read(A=10) | 10 | |
| 2 | read(A=10) | 10 | |
| 3 | A=A-5; write(A=5) | 5 | |
| 4 | A=A-8; write(A=2) | 2 |
T1 的修改结果 5 被 T2 基于旧值计算出的 2 覆盖了。它的问题不在“读了未提交数据”,而在多个事务都写同一数据,前一次写回被后一次写回抹掉。
不可重复读:两次验算不一致
不可重复读强调“重复读”。事务 T1 先读数据并做一次计算,准备稍后再读一次验证;中间 T2 修改并提交了数据,导致 T1 第二次读到的值不同。
| 步骤 | T1 | T2 |
|---|---|---|
| 1 | read(A=20) | |
| 2 | read(A=20); A=A+50; write(A=70); commit | |
| 3 | read(A=70) |
T1 两次读取同一数据,结果从 20 变成 70,验算失败,这就是不可重复读。它通常涉及另一个事务已经提交的修改。
读脏数据:读到后来被回滚的值
脏数据是未提交事务产生的中间数据。若 T1 修改 A 后尚未提交,T2 读取了这个修改值;随后 T1 回滚,T2 读到的值就变成了数据库中从未真正成立过的无效数据。
| 步骤 | T1 | T2 |
|---|---|---|
| 1 | read(A=20); write(A=50) | |
| 2 | read(A=50) | |
| 3 | rollback; A 恢复为 20 |
字幕提醒:看到 ROLLBACK,要警惕读脏数据。因为被读走的中间值可能随回滚消失。
三者对比
| 对比点 | 丢失修改 | 不可重复读 | 读脏数据 |
|---|---|---|---|
| 主要冲突 | 写-写 | 读-写-读 | 写未提交-读-回滚 |
| 是否强调两次读 | 否 | 是 | 否 |
| 是否强调回滚 | 否 | 通常否 | 是 |
| 典型关键词 | 覆盖、丢失更新 | 验算不一致、再次读取不同 | 未提交、回滚、脏数据 |
做题路线
- 先标出
read、write、commit、rollback。 - 两个事务都写同一数据且后写覆盖前写,判丢失修改。
- 同一事务两次读同一数据结果不同,判不可重复读。
- 读到了另一个事务未提交且后来回滚的数据,判读脏数据。
例题
一个事务读取了另一个事务尚未提交的数据,这属于:
两个事务先后修改同一数据,后一次修改覆盖前一次修改,属于:
自查要点
- 丢失修改和不可重复读的区别是什么?
- 读脏数据为什么和提交状态有关?
- 判断并发异常时应先标出哪些操作?