You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数据库Commit与Rollback的工作原理及并发更新丢失问题解析

数据库提交(Commit)与回滚(Rollback)的工作原理,以及并发更新丢失问题解析

先聊聊Commit和Rollback到底是干啥的——这俩都是数据库事务的"收尾操作",事务就像你去超市买东西的一整趟流程:拿商品、结账、拎走,要么全完成,要么中途放弃,不能只拿了商品不付钱就走。

提交(Commit)是怎么工作的?

当你在事务里执行了一堆修改(比如更新余额、插入订单),这些修改一开始其实只是存在数据库的内存缓冲区里,相当于你把商品放到购物车,但还没结账。只有当你执行COMMIT命令的时候,数据库才会把这些缓冲区里的修改永久写入磁盘,相当于结账完成,商品真正属于你了。同时,数据库会释放这个事务占用的各种锁,让其他线程/事务可以正常访问这些数据。一旦Commit成功,除非你做新的修改,否则这些数据就再也回不到之前的状态了——就算数据库突然崩溃重启,也能从磁盘里找到这些已经提交的变更。

回滚(Rollback)是怎么工作的?

如果在事务执行过程中出问题了(比如网络断了、代码报错,或者你突然不想买了),执行ROLLBACK就相当于把购物车清空,回到你没开始购物的状态。数据库会撤销这个事务里所有已经做的修改:比如你刚把余额从10改成5,Rollback之后余额就变回10;你插入了一条订单记录,Rollback之后这条记录就消失了。同时,事务持有的锁也会被释放,不会影响其他操作。


再说说你提到的并发更新丢失问题

咱们来模拟那个交错执行的场景,初始余额是10:
假设执行顺序是这样的:

  • 线程1先读余额:拿到10(步骤1.a)
  • 线程2紧接着也读余额:同样拿到10(步骤2.a)
  • 线程1计算后把余额改成5,写入数据库(步骤1.b+1.c),此时数据库里余额是5
  • 线程2还拿着之前读的10,计算后改成13,写入数据库(步骤2.b+2.c),此时数据库里余额变成13

结果就是线程1的提取操作完全被覆盖了——本来正确的结果应该是10-5+3=8,但现在变成13,这就是更新丢失。

为啥会出现这个问题?因为这两个线程的"读-改-写"不是原子操作,中间被对方插了一脚。每个线程都以为自己拿到的是最新数据,但其实在它们计算的过程中,数据已经被另一个线程修改了,最后写入的时候就把之前的修改覆盖了。

怎么解决?给你几个常见方案:

  • 把每个线程的操作包成一个带锁的事务:比如线程1开始事务后,先给余额行加排他锁,直到Commit或Rollback才释放,这样线程2就只能等线程1做完才能读数据
  • 用数据库的原子更新语句:比如直接执行UPDATE user_balance SET amount = amount - 5 WHERE user_id = 123,数据库会把读和写做成一个不可打断的原子操作,不会被其他线程插队
  • 用乐观锁:在余额表加一个版本号字段,每次更新的时候检查版本号是否和自己读的时候一致,比如UPDATE user_balance SET amount = 5, version = version +1 WHERE user_id=123 AND version=1,如果版本号不对就重试

内容的提问来源于stack exchange,提问作者Jus12

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:17:19