一般,用户的余额记录只有一行记录,这种场景的用户余额扣减比较容易,直接使用update就可以实现。
但是,在每次购买余额有时间期限的场景中(以及其他需要多行余额记录的场景中),余额就不能是一行记录,而是多行记录。每次购买都需要增加一行记录,这样才能跟踪每一行余额的截止时间。超过截止时间的余额,即使没有使用完,也是无效的。
在这种多行记录的场景中,更新会发生在一行记录,也会发生在两行记录,甚至多行记录。这种场景,update实现是有局限性的。
本文采用 获取排序列表依次扣减 的方式实现多行余额记录的扣减:首选查询能使用的余额记录,条件为不过期+有余额;对余额列表进行排序,这个排序就是扣减的先后顺序,比如是过期时间最早的在最前面;计算余额总和,校验是否足够,足够继续往下;遍历列表,依次扣减余额,扣减完每一行记录,都更新到数据库(优化:变动entity添加到List,扣减结束,batch更新),当需要扣减的总余额减为0时,结束遍历。
余额扣减过程需要最小化、原子化,否则会出现重复消费的错误。使用redisson.lock实现阻塞公平锁。
select
获取带扣减顺序的余额列表,校验是够足够
1 | // 查询用户链余额 |
reduce
执行用户余额的扣减,可能扣减一行,或多行
1 | public ReduceBalanceRecordDto reduceUserBalance(List<UserBalanceEntity> userBalanceEntityList) { |