支付系统的记账业务,需要登记每笔记账流水的账后余额。
在大规模并发条件下,简单使用乐观锁或者悲观锁都回严重的锁定数据库,导致性能变慢,下面介绍优化前和优化后的两种处理思路。
第一种方案,使用乐观锁
实现过程:
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
虽然触发器对数据库性能存在一定的损失,但这个好处还是明显的。