关于钱包扣减的问题
来源: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的更新操作会成为系统瓶颈,所以可以尝试利用一些读写分离,分库分表的方式去提升吞吐能力。
00