如果站在必须保证扣费和充值都成功的维度
* 用悲观锁的方式实现
使用用户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则表示更新失败(数据已经被其他线程更新)。