如何防止并发操作时账户余额的扣费和充值冲突?

作者站长头像
站长
· 阅读数 23

账户余额,充值和扣费,怎么防止在扣费的时候拿的余额是10块钱,要扣1块钱,然后余额是9块钱然后同一时间充值拿的余额也是10块钱,充值10块钱。应该是最后充完余额为19元,怎么防止这个并发呢,请问sql应该怎么去写

怎么防止这个并发呢

回复
1个回答
avatar
test
2024-06-19

如果站在必须保证扣费和充值都成功的维度

  • 用悲观锁的方式实现使用用户id做为锁标识,例如 change:balance:userId:123当变更账户余额时(充值或扣费)尝试获得锁,如果获得锁成功则继续变更账户余额,否则阻塞,直到尝试获得锁实施变更账户余额操作,从而保证整个变更余额过程的原子性;
  • 队列的方式实现通过队列来实现串行和解耦,将扣费和充值动作都丢到FIFO队列中,在由一个消费者依次去队列中取出要执行的action,这样保证整个更余额过程是同步的(synchronized);

    当然这两种方案也可以结合使用

如果站在高性能的维度

  • 可通过乐观锁方式实现即在获取账户余额时同时获得此时数据的verson,当变更余额时加上version的判断,例如

    udpate account_balance 
    set balance = balance + :changeNum, version = version + 1
    where user_id = :userId and version = :version
    :changeNum为传入的变更余额(扣1块或充值10块),:userId为传入的用户id,:version为传入的数据verison

此时,sql的响应行数为1则表示更新成,0则表示更新失败(数据已经被其他线程更新)。

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容