关于钱包扣减的问题

来源:11-6 礼物余额扣减流程讲解

光_cfstOQ

2025-10-06

老师 关于扣减的问题  课程中说的是先扣减redis  再扣减DB  那么如果在高并发场景下  redis连续扣减,但实际上DB没有连续扣减,导致redis和db的数据不统一 这种情况有什么手段来防止?

写回答

1回答

Danny_Idea

2025-10-12

间隔许久,我重新想了下当初这个方案链路,链路可以优化成这样:


链路改为先mysql事务扣减,mysql事务内部发送mq的事务消息,然后在mq的事务消息消费端做redis的decr操作。流程如下:

1. 开启 MySQL 本地事务

2. MySQL 中执行 “扣减余额 SQL”(带条件校验)

3. 事务内发送 MQ(确保 MQ 发送成功才提交事务)

4. 提交 MySQL 事务

5. 消费 MQ 消息,更新 Redis 余额

具体每个环节的细节如下:


1.MySQL 层:用 “条件 SQL” 确保扣减原子性。

这里可以合理运用一些乐观锁机制去扣减余额值,并且在update里面做余额是否充足的判断,例如:

UPDATE user_balance 
SET balance = balance - #{deductAmount}, 
    update_time = NOW()  -- 记录更新时间,用于后续校验
WHERE user_id = #{userId} 
  AND version = #{version}. -- 乐观锁
  AND balance >= #{deductAmount};  -- 关键:防止超扣


2.Redis 层:用 “原子命令” 更新缓存。

消费 MQ 更新 Redis 时,使用类似decr这样的原子性的命令进行操作。


3.MQ 消息可靠性保障

生产端:发送消息使用事务消息,确保不会因为事务提交失败而发送消息的情况。(事务消息可以基于本地事务表去做完善)。

消费端:开启 “手动 ACK”,消费 MQ 消息要在更新 Redis成功后,再手动确认 ACK,另外消费端一定要做好消费幂等的情况,避免超扣。


4.定期补偿巡检任务执行

设计定时任务(如每 5 分钟执行一次),对比 Redis 与 MySQL 的余额,发现不一致则修复。


总结:

这个方案存在1个缺点,就是如何在高并发场景下,mysql的更新操作会成为系统瓶颈,所以可以尝试利用一些读写分离,分库分表的方式去提升吞吐能力。


0
0

SpringCloudAlibaba高并发仿斗鱼直播平台实战

SpringCloudAlibaba高并发仿斗鱼直播平台实战

443 学习 · 361 问题

查看课程