面试题答案
一键面试- 开始事务:
START TRANSACTION;
- 对账户A加排他锁:
- 原因:为了防止其他事务在转账过程中修改账户A的余额,保证数据一致性。
- 操作:
这里SELECT balance FROM accounts WHERE account_id = 'A' FOR UPDATE;
FOR UPDATE
语句会对查询到的账户A记录加排他锁。 - 检查账户A余额是否足够:
- 读取账户A的余额,判断是否大于等于100。
- 例如:
SELECT balance INTO @a_balance FROM accounts WHERE account_id = 'A'; IF @a_balance >= 100 THEN -- 余额足够,继续转账操作 ELSE -- 余额不足,回滚事务 ROLLBACK; RETURN; END IF;
- 对账户B加排他锁:
- 原因:防止其他事务在转账过程中修改账户B的余额。
- 操作:
SELECT balance FROM accounts WHERE account_id = 'B' FOR UPDATE;
- 执行转账操作:
- 更新账户A和账户B的余额。
- 例如:
UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A'; UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
- 提交事务:
COMMIT;
在整个过程中,主要使用排他锁(X锁)来保证数据的一致性,没有用到共享锁(S锁)。因为共享锁主要用于读操作并发控制,而银行转账场景是写操作,需要排他锁来避免并发问题。如果在某些场景下,需要在读取账户余额时允许其他事务同时读取,可以在读取账户A和账户B余额时使用共享锁,但后续更新操作前仍然需要升级为排他锁。例如:
- 开始事务:
START TRANSACTION;
- 对账户A加共享锁:
SELECT balance FROM accounts WHERE account_id = 'A' LOCK IN SHARE MODE;
- 检查账户A余额是否足够:同上述步骤3。
- 将账户A的共享锁升级为排他锁:
SELECT balance FROM accounts WHERE account_id = 'A' FOR UPDATE;
- 对账户B加共享锁:
SELECT balance FROM accounts WHERE account_id = 'B' LOCK IN SHARE MODE;
- 将账户B的共享锁升级为排他锁:
SELECT balance FROM accounts WHERE account_id = 'B' FOR UPDATE;
- 执行转账操作和提交事务:同上述步骤5和6。