功能说明
该示例用版本列实现乐观锁:更新语句把读到的版本放进 WHERE 条件,只有存储版本仍等于期望版本才执行。成功时版本加一,因此每次写入都推进版本。谓词同时包含余额约束,防止扣款使余额为负。应用依据受影响行数判断结果。
关键代码
版本比对与业务约束合并在同一条语句里,数据库在同一事务内原子地求值,避免先检查后更新之间的竞态。影响一行即成功;影响零行有两种可能——版本已变或余额不足——因此需要再读一次把两者区分开。重试前必须先取回最新版本,否则会再次失败或覆盖他人改动。
使用注意
零行结果不能简单当作错误抛给用户,应重读、重放业务决策再重试,或明确告知冲突。该语句必须在一个事务中执行,且应用应正确处理事务边界,避免部分提交。版本号用整数并严格自增,不要用时间戳,因为同刻度内两次写入无法区分。写密集场景下冲突会变多,此时悲观锁或串行化隔离可能更合适。
代码示例
-- 乐观锁更新:版本匹配才写入,且不允许透支。
UPDATE accounts
SET balance = balance - :amount,
version = version + 1
WHERE id = :account_id
AND version = :expected_version
AND balance >= :amount;
-- 应用侧检查受影响行数:
-- 1 -> 更新成功
-- 0 -> 并发冲突,或余额不足(需再读一次以区分)
-- 取回最新版本用于重试:
SELECT id, balance, version FROM accounts WHERE id = :account_id;