一.账户余额在高并发更新的时候,扣减会出现负数问题?
思路,在sql语句的条件中,设置更新金额减去账户余额一定大于0,对于余额的更新,不使用乐观锁,也没有用查询到的余额作为可以更新的条件,而是设置更新金额减去账户余额一定大于0 ,这是能够高并发更新的关键,因为只有账户的余额不被扣成负数即可。
另外,防止高并发余额账户查询的余额已经被其他线程更新,使用行级锁,所以对于查询语句添加for update
例子:
//查询账户的语句
<select id="getAccountInfoByCustomerNo" resultMap="AccountInfoEntityMap">
SELECT ID,ACCOUNT_TYPE,ACCOUNT_STATUS,MERCHANT_NO,ACCT_NO,BALANCE
FROM TBL_ACCOUNT_INFO
where 1=1 AND MERCHANT_NO = #{customerNo} and ACCOUNT_TYPE =#{accountType} AND ACCOUNT_STATUS = 'ACTIVATION' FOR UPDATE
</select>
<!--更新入款余额-->
<update id="updateInBalance">
UPDATE TBL_ACCOUNT_INFO
<set>
BALANCE = BALANCE + #{amount,jdbcType=DECIMAL},
UPDATE_TIME =CURRENT TIMESTAMP
</set>
WHERE ACCT_NO = #{acctNo,jdbcType=VARCHAR}
</update>
<!--更新出款余额-->
<update id="updateOutBalance" >
UPDATE TBL_ACCOUNT_INFO
<set>
BALANCE = BALANCE - #{amount,jdbcType=DECIMAL},
UPDATE_TIME =CURRENT TIMESTAMP
</set>
WHERE ACCT_NO = #{acctNo,jdbcType=VARCHAR} AND BALANCE - #{amount,jdbcType=DECIMAL} >= 0
</update>
重点注意,查询和更新的操作请在一个事物中控制。
二、由于高并发导致本次的查询的账户余额更新之后,添加账户历史的时候如何一一对应?
解决思路,在更新完余额后,重新再次查询余额,然后记录账户历史。来保证数据的准确性。
关于账户历史表的设计中,我们只有进账的方式,本次交易金额,和剩余账户余额,而不记录操作前余额,也是基于高平发的思路。
以下是我们账务历史表的设计:
TBL_ACCOUNT_HISTORY 账户历史表
字段 | 类型 | 备注 |
---|---|---|
ID | int类型 从0开始自增 | 唯一主键 |
RELATED_TRANS_ID | VARCHAR(32) | 交易订单ID |
ACCOUNT_ID | VARCHAR(32) | 账户id |
DIRECTION | VARCHAR(32) | 资金方向(IN,OUT) |
AMOUNT | DECIMAL(18,2) | 当期交易金额 |
BALANCE | DECIMAL(18,2) | 当期账户余额 |
TRX_TYPE | VARCHAR(32) | 交易类型(充值,转账,代付,提现) |
CREATE_DATE | DATE | 创建时间 |
MERCHANT_NO | VARCHAR(32) | 商户编号 |
REASON | VARCHAR(32) | 原因 |
TBL_ACCOUNT_INFO 账户信息表
字段 | 类型 | 备注 |
---|---|---|
ID | int类型 从0开始自增 | 唯一主键 |
ACCOUNT_TYPE | VARCHAR(32) | 账户类型 |
ACCOUNT_STATUS | VARCHAR(32) | 账户状态 |
BALANCE | DECIMAL(18,2) | 账户金额 |
CLOSE_TIME | TIMESTAMP | 关闭时间
|
CREATE_TIME | TIMESTAMP | 创建时间 |
UPDATE_TIME | TIMESTAMP | 更新时间 |
MERCHANT_NO | VARCHAR(32) | 商户编号 |
ACCOUNT_NO | VARCHAR(32) | 账户编号 |
CURRENCY_ENUM | VARCHAR(32) | 币种 |