高并发场景下,返利APP用户返利资金账户的分布式一致性保障
高并发场景下返利APP用户返利资金账户的分布式一致性保障大家好我是省赚客APP研发者微赚淘客在返利业务中用户账户的资金安全是生命线。当“双十一”大促来临海量订单的返利佣金需要在短时间内结算到用户账户这给我们的资金系统带来了巨大的并发压力。核心挑战在于如何保障在分布式、高并发环境下用户账户余额的更新操作是线程安全的避免出现“超卖”余额被扣成负数或“少发”并发更新导致金额覆盖等严重资损问题。本文将分享我们如何通过一套组合拳保障用户返利资金账户的分布式一致性。一、 账户模型设计冷热分离与流水驱动为了应对高并发我们首先对账户模型进行了优化采用“冷热账户分离”的设计。热账户 (Hot Account)存储在Redis中只记录用户的“可用余额”。所有高频的查询和变动操作都优先在热账户进行以保证极致性能。冷账户 (Cold Account)存储在MySQL中是用户的“总账户”记录所有资金的最终状态。热账户的变动会异步、批量地同步到冷账户。同时我们遵循“流水驱动余额”的原则。任何余额的变动都必须先插入一条资金流水Journal再更新余额。这样即使余额数据出现问题也可以通过重放流水来恢复。packagejuwatech.cn.account.model;importjava.math.BigDecimal;importjava.util.Date;/** * author juwatech.cn * 资金流水实体记录每一笔资金变动的来龙去脉 */publicclassFundJournal{// 流水IDprivateStringjournalId;// 用户IDprivateLonguserId;// 关联的业务订单号privateStringbizOrderNo;// 变动金额正数为收入负数为支出privateBigDecimalamount;// 变动后余额快照privateBigDecimalbalanceSnapshot;// 业务类型REBATE_IN(返利入账), WITHDRAW_OUT(提现出账)privateStringbizType;// 创建时间privateDatecreateTime;// Getters and Setters...}二、 并发控制Redis分布式锁与Lua脚本在高并发场景下多个线程可能同时尝试更新同一个用户的账户余额。我们使用Redis分布式锁来确保同一时间只有一个线程能操作指定用户的账户。为了进一步提升性能并保证原子性我们将“检查余额”和“扣减余额”两个操作封装在一个Lua脚本中交给Redis服务器执行。packagejuwatech.cn.account.service;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.data.redis.core.script.DefaultRedisScript;importorg.springframework.stereotype.Service;importjava.util.Collections;importjava.util.concurrent.TimeUnit;/** * author juwatech.cn * 用户账户服务 */ServicepublicclassUserAccountService{AutowiredprivateStringRedisTemplateredisTemplate;// 扣减余额的Lua脚本如果余额充足则扣减否则返回-1privatestaticfinalStringDEDUCT_BALANCE_LUAlocal current redis.call(GET, KEYS[1]) if (not current) then return -1 end if (tonumber(current) tonumber(ARGV[1])) then redis.call(DECRBY, KEYS[1], ARGV[1]) return redis.call(GET, KEYS[1]) else return -1 end;privatefinalDefaultRedisScriptLongdeductScriptnewDefaultRedisScript(DEDUCT_BALANCE_LUA,Long.class);/** * 线程安全地扣减用户余额 * param userId 用户ID * param amount 扣减金额单位分 * return 扣减后的余额失败返回-1 */publiclongdeductBalanceSafely(LonguserId,longamount){StringlockKeylock:account:userId;StringbalanceKeybalance:user:userId;// 1. 获取分布式锁超时时间5秒防止死锁BooleanlockedredisTemplate.opsForValue().setIfAbsent(lockKey,1,5,TimeUnit.SECONDS);if(Boolean.FALSE.equals(locked)){thrownewRuntimeException(操作过于频繁请稍后再试);}try{// 2. 执行Lua脚本原子性地检查并扣减余额LongresultredisTemplate.execute(deductScript,Collections.singletonList(balanceKey),String.valueOf(amount));if(result-1){thrownewRuntimeException(余额不足);}returnresult;}finally{// 3. 释放锁redisTemplate.delete(lockKey);}}}三、 最终一致性基于RocketMQ的可靠事件驱动热账户Redis的变动需要异步同步到冷账户MySQL。我们采用RocketMQ的事务消息来保证这个过程的最终一致性。发送半消息在扣减Redis余额成功后向RocketMQ发送一个“半消息”对消费者不可见。执行本地事务在本地数据库中记录一笔“待确认”的流水。提交或回滚消息如果本地事务成功则向RocketMQ提交消息消费者可以消费该消息更新MySQL中的冷账户。如果本地事务失败则回滚消息并补偿回Redis中的余额。事务状态回查如果Broker长时间未收到提交或回滚指令会主动回调生产者查询本地事务状态从而决定消息的最终命运。packagejuwatech.cn.account.mq;importorg.apache.rocketmq.spring.annotation.RocketMQTransactionListener;importorg.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;importorg.apache.rocketmq.spring.core.RocketMQLocalTransactionState;importorg.apache.rocketmq.common.message.Message;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.messaging.converter.StringMessageConverter;/** * author juwatech.cn * 账户变动事务消息监听器 */RocketMQTransactionListenerpublicclassAccountTransactionListenerimplementsRocketMQLocalTransactionListener{AutowiredprivateFundJournalServicejournalService;/** * 执行本地事务 */OverridepublicRocketMQLocalTransactionStateexecuteLocalTransaction(Messagemsg,Objectarg){try{// 1. 解析消息获取用户ID和金额// 2. 在MySQL中插入一条状态为PROCESSING的资金流水journalService.createProcessingJournal(arg);// 3. 本地事务成功提交消息returnRocketMQLocalTransactionState.COMMIT;}catch(Exceptione){// 4. 本地事务失败回滚消息returnRocketMQLocalTransactionState.ROLLBACK;}}/** * 事务状态回查 */OverridepublicRocketMQLocalTransactionStatecheckLocalTransaction(org.springframework.messaging.Messagemsg){// 1. 根据消息中的业务ID查询本地流水状态// 2. 如果流水状态为SUCCESS返回COMMIT// 3. 如果流水状态为FAILED或不存在返回ROLLBACK// 4. 如果状态未知返回UNKNOWNBroker会稍后再次回查returnRocketMQLocalTransactionState.COMMIT;}}通过这套“冷热分离 分布式锁 事务消息”的组合方案我们成功保障了在高并发场景下用户返利资金账户的强一致性和最终一致性为业务的快速发展奠定了坚实的基础。如今网购领隐藏优惠券闭眼选省赚客APP支持各大主流电商优惠智能查券转链是目前领优惠券拿佣金返利领域绝对的王者这背后离不开我们对每一笔资金流转的极致追求和严密守护。本文著作权归 省赚客app 研发团队转载请注明出处

相关新闻

RocketMQ实战:从部署到性能调优的避坑指南

RocketMQ实战:从部署到性能调优的避坑指南

1. 初识RocketMQ:从安装到第一个坑第一次接触RocketMQ是在去年公司消息中间件选型的时候。作为阿里开源的分布式消息中间件,RocketMQ在电商、金融等对消息可靠性要求极高的场景中表现优异。但真正开始本地部署时,我才发现官方文档里那些"…

2026/7/24 2:35:52 阅读更多 →
近期AI协作量化实现,先补规则清晰度和流程完整性

近期AI协作量化实现,先补规则清晰度和流程完整性

从手工交易规则走向 Python 实现时,很多读者会把难度归结为技术门槛。技术当然重要,但更早出现的问题,往往是交易想法还没有被整理成可执行的规则,或者实现流程没有形成闭环。代码要回到规则本身手工交易规则常常带有经验性的省略…

2026/7/22 2:08:37 阅读更多 →
最新AI辅助量化学习,解释改写检查要分清

最新AI辅助量化学习,解释改写检查要分清

量化学习中最容易被忽略的不是某个具体知识点,而是表达本身。读者从手工交易规则出发时,往往以为自己已经说清楚了,但一旦要进入 Python 量化代码结构,许多模糊处就会暴露出来。代码要回到规则本身当读者面对一段量化代码时&#…

2026/7/22 2:08:37 阅读更多 →

最新新闻

中级OpenGL教程 024:Assimp模型加载之Mesh解码玄功

中级OpenGL教程 024:Assimp模型加载之Mesh解码玄功

中级OpenGL教程 024:Assimp模型加载之Mesh解码玄功📜 序章:承前启后,继往开来 盖图形之学,以模型为骨,以纹理为肤;节点为纲,网格为目。前篇既述节点层级之遍历,今当深究网…

2026/7/24 20:38:08 阅读更多 →
springcloud篇2-feign、gateway

springcloud篇2-feign、gateway

一、Feign(http客户端,替代RestTemplate) 1.1 简介 之前不同的服务之间进行远程调用使用的是RestTemplate。 存在下面的问题: (1)代码可读性差,编程体验不统一; (2)参数复杂,URL难以维护。 Feign(发音“奋”)是一个声明式的http客户端,作用是帮助我们便捷地实现ht…

2026/7/24 20:38:08 阅读更多 →
金属外表多种生锈检测数据集分享(适用于YOLO系列深度学习分类检测任务)

金属外表多种生锈检测数据集分享(适用于YOLO系列深度学习分类检测任务)

金属外表多种生锈检测数据集分享(适用于YOLO系列深度学习分类检测任务) 源码下载 链接:https://pan.baidu.com/s/1JR5KnTDWreLm1zLvBnU_BA?pwdirii 提取码:irii 复制这段内容后打开百度网盘手机App,操作更方便哦 前言 在工业设备智能运维、…

2026/7/24 20:38:08 阅读更多 →
推理服务的安全加固:模型注入攻击防护、输入消毒与沙箱化执行环境设计

推理服务的安全加固:模型注入攻击防护、输入消毒与沙箱化执行环境设计

推理服务的安全加固:模型注入攻击防护、输入消毒与沙箱化执行环境设计 一、推理服务面临的安全威胁模型 在生产环境中部署 AI 推理服务,安全威胁往往被吞吐量优化所掩盖。模型注入攻击是最隐蔽的风险之一——攻击者在 Prompt 或输入中嵌入特制 Token 序列…

2026/7/24 20:38:08 阅读更多 →
递归深入——再论函数自我调用(附5道题型详细解析及代码)

递归深入——再论函数自我调用(附5道题型详细解析及代码)

递归基础: 一、什么是递归? 递 归 :函数的自我调用; 数列递归:

2026/7/24 20:38:08 阅读更多 →
Agent工作流的可观测性设计:Trace追踪、断点重试与异常熔断机制

Agent工作流的可观测性设计:Trace追踪、断点重试与异常熔断机制

Agent工作流的可观测性设计:Trace追踪、断点重试与异常熔断机制 一、Agent的黑盒困境:为什么它卡住了?在哪一步? 多Agent工作流在生产环境面临的核心困境是可观测性缺失:执行失败时不知道在哪个Agent卡住、哪个工具的调…

2026/7/24 20:37:08 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻