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

SQL Server事务问题:银行账户转账如何保证原子性执行?

解决银行账户转账的原子性与并发问题

嘿,作为SQL新手碰到这种转账问题太常见啦!你遇到的情况本质是没有把两个转账操作放在同一个原子事务里,而且没做好并发控制,导致中间可能被其他事务打断,出现“钱转出去但没收到”的不一致状态。下面给你一步步讲怎么搞定:

核心思路:用事务保证原子性

转账是典型的“要么全成,要么全败”的操作——从账户1扣钱和给账户2加钱必须作为一个整体执行,不能只完成其中一步。SQL里的事务就是干这个的,它能保证一组操作的原子性、一致性、隔离性和持久性(ACID)。

具体实现步骤

假设你用的是支持事务的数据库引擎(比如MySQL的InnoDB,这是银行系统常用的,MyISAM不支持事务哦),可以这么写:

-- 开启事务
START TRANSACTION;

-- 第一步:从账户1扣100美元
UPDATE accounts 
SET balance = balance - 100 
WHERE id = 1;

-- 第二步:给账户2加100美元
UPDATE accounts 
SET balance = balance + 100 
WHERE id = 2;

-- 如果上面两步都没报错,提交事务,永久生效
COMMIT;

-- 如果中间有任何错误,回滚事务,恢复到操作前的状态
-- ROLLBACK;

防止并发插队的关键

为了避免其他事务在你的转账过程中修改这两个账户的余额,有两个关键点:

  • 确保事务的隔离级别:默认的REPEATABLE READ(可重复读)隔离级别已经能防止脏读、不可重复读,但如果要更严格,可以用SERIALIZABLE(串行化),不过性能会稍低,一般银行系统用可重复读就够了。
  • 显式锁定账户:如果担心并发下的问题,可以在更新前先锁定两个账户,比如:
START TRANSACTION;

-- 先锁定两个账户,防止其他事务修改
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
SELECT balance FROM accounts WHERE id = 2 FOR UPDATE;

-- 执行转账操作
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;

COMMIT;

SELECT ... FOR UPDATE会给选中的行加排他锁,直到事务结束才释放,这样其他事务就没法修改这两个账户了,完美避免中间插队的问题。

你之前出错的原因分析

你之前出现的异常,大概率是:

  1. 没有把两个UPDATE放在同一个事务里,而是分开执行了,导致第一个UPDATE提交后,第二个UPDATE失败了,没法回滚。
  2. 数据库开启了自动提交(autocommit=1),每个SQL语句都是单独的事务,相当于两个独立操作,自然没法保证原子性。

额外注意事项

  • 一定要确认你的表引擎支持事务,比如MySQL里用SHOW CREATE TABLE accounts;看引擎是不是InnoDB。
  • 业务代码里要捕获SQL执行的错误,一旦出错就执行ROLLBACK,别让事务挂着。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:10:55