知识卡片

并发转账取款同时读旧值导致丢失修改

支付系统案例卡

内容

账户C1余额100元,事务Ti(转账50元到C2)和事务Tj(取款30元)几乎 同时开始:两者都先读到C1余额为100元(此时都还没有任何一方写入 修改),随后Ti计算出新余额50元并写入数据库,Tj基于自己读到的 旧值100元计算出新余额70元、随后写入数据库,覆盖了Ti刚写入的50元。 最终数据库里记录的C1余额是70元,但账户实际总共被转出/取出了 50+30=80元,正确的余额应该是20元——Ti对余额的那次修改被Tj的写入 “覆盖”,凭空消失了,账户记录与真实资金流出金额对不上。根因不是 “两个事务分别都算错了”(各自的加减法都是对的),而是两个事务在 数据库允许它们并发读到同一个旧值、又允许它们各自基于这个旧值独立 写入的情况下,后写入的一方无意中抹去了先写入一方的成果——这是 典型的”读后写非原子”竞态问题:中间隔着”读旧值→算新值→写新值” 这段时间窗口,任何在这段窗口内插入的并发写操作都可能被静默覆盖, 而不会有任何报错提示这次修改已经丢失。

参考来源

- 位置:《数据库原理(微课版)》第11章《事务处理技术》11.4节"事务 隔离级别"(源文件:_epub-src/index_split_007.html) - 结论依据:原文用银行账户C1余额100元、事务Ti转账50元、事务Tj取款 30元的具体场景,说明"两个事务共从账户C1转出50+30=80元,但系统中 记录的账户余额却是70元。事务Ti对账户余额的修改丢失了",明确点出 了并发读同一旧值、后写覆盖先写导致资金记录与实际不符的根因。 - 原始内容:对银行来讲,账户C1中余额为100元,若事务Ti读到C1账户 余额100元,此时事务Tj读C1账户余额,同样也是100元。若此时事务Ti 转账50元至账户C2,则写入C1账户余额50元,而接下来若事务Tj取款30 元,其修改C1账户余额为70元,并写入数据库,那么明明两个事务共从 账户C1转出50+30=80元,但系统中记录的账户余额却是70元。事务Ti对 账户余额的修改丢失了。