优惠券app的优惠券分发限流与Redis原子操作实现

发布时间:2026/9/15 17:40:32

优惠券app的优惠券分发限流与Redis原子操作实现 优惠券app的优惠券分发限流与Redis原子操作实现大家好我是省赚客APP研发者微赚淘客在优惠券App的运营活动中秒杀、限时抢券等高并发场景是家常便饭。当海量用户在同一时刻涌入试图领取数量有限的优惠券时系统将面临巨大的压力。如果处理不当不仅会导致优惠券被超发造成资损还可能因数据库压力过大而引发服务雪崩。本文将深入探讨如何利用Redis的原子操作构建一个高效、可靠的优惠券分发与限流系统确保在高并发下数据的一致性与服务的稳定性。一、问题剖析高并发下的超发与性能瓶颈优惠券分发的核心逻辑看似简单检查优惠券是否还有剩余。如果有则为用户创建一张优惠券并将剩余数量减一。在单机或低并发环境下这段逻辑没有问题。但在高并发场景下它会引发两个致命问题1. 超发问题假设有100张优惠券剩余1张。此时用户A和用户B同时发起领取请求。请求A和请求B几乎同时到达服务器并都执行了“检查剩余数量”的操作。由于操作尚未完成两个请求读取到的剩余数量都是1。两个请求都认为还有库存于是都继续执行“创建优惠券”和“库存减一”的操作。最终1张优惠券被发给了2个用户造成了超发。这就是典型的“检查后执行”Check-Then-Act竞态条件。2. 数据库性能瓶颈上述的检查和更新操作通常会涉及数据库的读写。在高并发下大量的请求会直接冲击数据库导致连接池耗尽、查询缓慢最终拖垮整个服务。二、解决方案Redis原子操作与Lua脚本解决上述问题的关键在于将“检查”和“执行”这两个非原子操作合并为一个原子操作。Redis凭借其单线程的事件处理模型和强大的原子命令成为了解决此问题的理想选择。1. 核心思想我们将优惠券的库存信息缓存在Redis中。用户的领券请求不再直接访问数据库而是先在Redis中执行一个原子性的扣减操作。只有当Redis中的扣减成功后才异步地去数据库中创建优惠券记录。2. 为什么选择Lua脚本虽然Redis的DECR、INCR等命令是原子的但“检查库存是否大于0然后减1”是一个复合逻辑需要多条命令才能完成。为了保证这个复合逻辑的原子性我们使用Redis的Lua脚本功能。Redis在执行Lua脚本时会将整个脚本作为一个整体来执行期间不会被其他命令打断。这完美地满足了我们的需求。三、核心代码实现1. 优惠券库存的Redis操作服务packagejuwatech.cn.coupon.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.List;/** * 优惠券Redis操作服务负责处理高并发下的库存扣减。 * author juwatech.cn */ServicepublicclassCouponRedisService{AutowiredprivateStringRedisTemplateredisTemplate;// 定义Lua脚本实现“检查并扣减”的原子操作privatestaticfinalStringDEDUCT_STOCK_LUA_SCRIPTlocal stock tonumber(redis.call(GET, KEYS[1])) if not stock then return -1 // 优惠券不存在end if stock 0 then return 0 // 库存不足end redis.call(DECR, KEYS[1]) return 1 ;// 扣减成功privatefinalDefaultRedisScriptLongdeductStockScript;publicCouponRedisService(){deductStockScriptnewDefaultRedisScript();deductStockScript.setScriptText(DEDUCT_STOCK_LUA_SCRIPT);deductStockScript.setResultType(Long.class);}/** * 尝试扣减指定优惠券的库存。 * param couponKey Redis中存储优惠券库存的Key * return 1: 扣减成功, 0: 库存不足, -1: 优惠券不存在 */publiclongtryDeductStock(StringcouponKey){// 执行Lua脚本KEYS[1]就是couponKeyLongresultredisTemplate.execute(deductStockScript,Collections.singletonList(couponKey));returnresultnull?-1:result;}}2. 优惠券领取的业务逻辑服务packagejuwatech.cn.coupon.service;importjuwatech.cn.coupon.model.UserCoupon;importjuwatech.cn.coupon.dao.UserCouponDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;/** * 优惠券核心业务服务。 * author juwatech.cn */ServicepublicclassCouponService{AutowiredprivateCouponRedisServicecouponRedisService;AutowiredprivateUserCouponDaouserCouponDao;// 优惠券在Redis中的Key前缀privatestaticfinalStringCOUPON_STOCK_KEY_PREFIXcoupon:stock:;/** * 用户领取优惠券的入口方法。 * param userId 用户ID * param couponId 优惠券活动ID * return 领取结果信息 */publicStringgrabCoupon(LonguserId,LongcouponId){StringcouponKeyCOUPON_STOCK_KEY_PREFIXcouponId;// 1. 首先尝试在Redis中扣减库存longresultcouponRedisService.tryDeductStock(couponKey);if(result1){// 2. Redis扣减成功异步或同步地创建用户优惠券记录// 为了保证数据最终一致性这里通常会发送到MQ由消费者异步处理入库// 此处为简化逻辑直接调用同步方法booleansavedsaveUserCoupon(userId,couponId);if(saved){return恭喜抢券成功;}else{// 理论上不会失败如果失败需要有补偿机制如回滚Redis库存return抢券成功但记录发放失败请联系客服。;}}elseif(result0){return抱歉优惠券已被抢光。;}else{return抱歉优惠券活动不存在。;}}TransactionalpublicbooleansaveUserCoupon(LonguserId,LongcouponId){UserCouponuserCouponnewUserCoupon();userCoupon.setUserId(userId);userCoupon.setCouponId(couponId);// ... 设置其他属性userCouponDao.insert(userCoupon);returntrue;}}3. 优惠券分发的限流策略除了防止超发我们还需要对整个领券接口进行限流以保护后端服务不被突发流量击垮。可以基于Redis的INCR和EXPIRE命令实现一个简单的滑动窗口限流。packagejuwatech.cn.coupon.service;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.stereotype.Service;importjava.util.concurrent.TimeUnit;/** * 基于Redis的简单限流服务。 * author juwatech.cn */ServicepublicclassRateLimiterService{AutowiredprivateStringRedisTemplateredisTemplate;/** * 尝试获取一个令牌。 * param key 限流的Key例如 rate_limit:grab_coupon * param limit 时间窗口内允许的最大请求数 * param window 时间窗口大小 * param unit 时间单位 * return true: 允许请求, false: 请求过于频繁 */publicbooleantryAcquire(Stringkey,intlimit,longwindow,TimeUnitunit){// 1. 对Key进行自增操作LongcountredisTemplate.opsForValue().increment(key);if(count!nullcount1){// 2. 如果是第一个请求则设置过期时间形成一个时间窗口redisTemplate.expire(key,window,unit);}// 3. 判断计数是否超过限制returncount!nullcountlimit;}}通过在Controller层调用RateLimiterService我们就可以轻松地为抢券接口加上保护伞。这套“Redis原子操作 限流”的组合方案是应对高并发优惠券分发场景的黄金标准。它利用Redis的高性能和原子性在内存中快速完成库存扣减将绝大部分无效请求拦截在数据库之外从而保证了系统的稳定性和数据的一致性。网购领隐藏优惠券就用省赚客APP支持各大主流电商优惠智能查券转链是目前领优惠券拿佣金返利领域绝对的王者其流畅的抢券体验背后正是这套强大的技术架构在保驾护航。本文著作权归 省赚客app 研发团队转载请注明出处
延伸阅读

更多相关文章

2026/9/8 23:00:20

优惠券返利app的混合缓存架构与热点Key解决方案

优惠券返利app的混合缓存架构与热点Key解决方案 大家好,我是省赚客APP研发者微赚淘客! 在优惠券返利App中,商品详情、优惠券信息和用户返利数据是访问频率最高的核心数据。面对高并发的用户请求,单一依赖数据库查询无异于自寻死路…

2026/9/12 5:40:01

计算机单片机毕设实战-基于 STM32 的货物超重检测与报警装置研发 基于嵌入式平台的阈值可调式称重报警器设计(013701)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/9/8 23:00:07

科研AI会员深度体验:7天足够判断一个工具适不适合

相信绝大多数科研人都有同款纠结:想入手一站式科研平台,又怕年费不便宜、功能与自身课题不匹配,贸然充值只会白白浪费预算。网上零散的试用渠道要么功能阉割,要么时长太短,很难完整体验整套工具的真实使用效果。我当初…

2026/9/15 17:38:12

基于MATLAB的光伏阴影多峰P-V特性曲线建模与MPPT仿真

简介:资源围绕光伏特性曲线、阴影遮挡与MPPT最大功率点跟踪,提供一套MATLAB/Simulink仿真模型,面向光伏系统设计人员、电气工程学生及MPPT算法研究者。压缩包共6个文件,核心为slx与mdl仿真模型,附mat数据文件、slxc仿真…

2026/9/15 4:54:30

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/15 0:01:16

AI英语单词APP开发:自适应学习算法与移动端优化实践

1. 项目概述 作为一名在移动应用开发领域摸爬滚打多年的老手,我最近完成了一个AI英语单词APP的开发项目。这个项目将传统单词记忆方法与现代AI技术相结合,打造了一款能够智能适应不同用户学习习惯的英语学习工具。 市面上大多数单词APP都存在一个通病&a…

2026/9/15 0:01:16

Flutter与OpenHarmony结合开发手语学习APP实战

1. 项目背景与核心价值作为一名同时接触过Flutter和OpenHarmony的开发者,最近我完成了一个基于Flutter for OpenHarmony的手语学习APP实战项目。这个项目最大的特点在于实现了跨平台框架与国产操作系统深度结合的创新实践——用Flutter开发的应用能完美运行在OpenHa…

2026/9/15 0:01:16

六个月成为机器人工程师:从ROS2到SLAM的实战路径

1. 六个月的紧迫感从哪来:先搞清楚你要成为哪种机器人工程师说实话,六个月的期限并不是一个宽松的时间线。市面上任何一本正经的机器人学教材都超过五百页,ROS2的官方文档可以翻到你怀疑人生,再加上ABB、KUKA这些工业机器人厂家动…

2026/9/15 14:22:53

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/14 13:53:59

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/15 11:42:23

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码