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

Posted hifong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一种登记账后余额的处理方法相关的知识,希望对你有一定的参考价值。

支付系统的记账业务,需要登记每笔记账流水的账后余额。
在大规模并发条件下,简单使用乐观锁或者悲观锁都回严重的锁定数据库,导致性能变慢,下面介绍优化前和优化后的两种处理思路。

第一种方案,使用乐观锁
实现过程:
Step1:从账户中获取最近余额以及账户当前版本号,代码如下:
Select version, balance from account where customer_no=?
Step2:创建账户流水
Insert into account_flow(version, amount, balance, customer_no) values(:version+1, amount, balance+amount, customer_no)
Step3:更新账户余额
Update account set version=version+1, balance = balance + amount where customer_no=:customer_no and version=:version
在并发量小的时候,这样写基本没有问题,可以保证每笔交易的记账流水都是OK,然而,并发大的时候,会发现Step3中的红色部分的条件,完全Hold不住了,还没等待一笔交易完成,可能version已经变化了不止一次

第二种方案,使用触发器
实现过程:
Step1:创建过程表
Create table account_his(account_id, version, pre_version, balance, pre_balance, uid)
字段分别是:账户ID、当前版本号、前一版本号、当前余额、前一版本余额、唯一ID(后面解释)
Step2:创建针对account的触发器
Create or replace trigger account_change_trigger before update on account for each row
Begin
  insert into account_his values (:old.account_id,:new.version,:old.version,:new.balance, :old.balance,:new.uid)
End
Step3:修改记账过程
1、生成uid=uuid
2、更新账户余额:update account set uid=:uid,balance=balance+amount where account_id=xx
3、登记记账流水:insert into account_flow values(account_id, amount, uid),注,此处暂时不登记余额
4、使用定时器,从account_his中更新account_flow的余额:update account_flow set balance=account_his.balance where account_his.uid=account_flow.uid

虽然触发器对数据库性能存在一定的损失,但这个好处还是明显的。

以上是关于一种登记账后余额的处理方法的主要内容,如果未能解决你的问题,请参考以下文章

谈论并发 PHP 时的最佳方法

债务重组的会计处理方法

MySQL事务处理实现方法步骤

MySQL事务处理实现方法步骤

以太坊系节点余额精度处理

REST API 设计 - 提现余额动作