多并发时支付如何保持账户余额的一致性?

Posted LOENG

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多并发时支付如何保持账户余额的一致性?相关的知识,希望对你有一定的参考价值。

转载:http://costlend.com/2016/03/14/dispatch-pay-balance-keep-consistence/


不管是电商,还是O2O业务都会涉及到支付,而且多速情况下流量比较大,尤其是在做活动的时候。一般支付系统主要有充值,扣费,提现,转账等功能,那么在有些业务场景下,尤其是多并发的情况下,我们在做扣费业务操作时该怎样去保持账户余额的一致呢?

Java开发人员可能第一个想法就是在调用扣减的DAO的方法上加上一个synchronized关键字,这个解决办法在单节点应用部署是也许能生效管用,但是在我们实际的应用场景中,一般都是集群,多节点部署的应用,这个时候该如何解决呢?

我们有一张账户表tb_account

fieldtypedesc
uid bigint 用户id
balance decimal 余额
update_time datetime 表数据更新时间

扣费之前,我们要先查询一下账户的余额是否足够抵扣,然后再做真正的减扣。
大致的过程如下:

  1. select balance from tb_account where uid=100;
  2. 程序判断balance的值是否足够抵扣。
  3. update tb_account set balance = balance - 28.00, update_time = sysdate() where uid=100;
    通常情况下,这种余额判断方法在高并发且不加锁的情况下是非常不可靠的。所以在做扣费操作时要考虑到并发扣费的情况,允许让其并发扣费,但是不应该允许账户余额为负数。转账的话也是一样,相当于先从一个账户扣费,再给另一个账户充值,都必须要在一个事务内完成。
    可以使用一个存储过程来把这些步骤统一起来。下面的存储过程亲测可用。
 1 create procedure proc_account_balance_dec ( in_money decimal(8,2), in_uid bigint, OUT status int )  
 2 BEGIN  
 3 
 4 DECLARE from_account_balance decimal(8,2);  
 5 
 6 START TRANSACTION;  
 7 
 8 SELECT balance INTO from_account_balance FROM tb_account  
 9     WHERE uid = in_uid FOR UPDATE;  
10 
11 IF from_account_balance>=in_money THEN   
12     UPDATE tb_account SET balance = balance - in_money , update_time = sysdate()  
13         WHERE uid = in_uid;  
14     COMMIT;  
15 
16     SET status=1;  
17 ELSE   
18     ROLLBACK;  
19     SET status=0;  
20 END IF;  

 

 


以上是关于多并发时支付如何保持账户余额的一致性?的主要内容,如果未能解决你的问题,请参考以下文章

如何模拟账户余额检查? (最终一致性/事件驱动架构是不是可能?)

一种登记账后余额的处理方法

浅析微信支付:(余额提现)企业付款到微信用户零钱或银行卡账户

当记账系统遇到并发

Java如何实现对Mysql数据库的行锁

并行与并发的理解