发布时间:2026/7/30 17:43:23
优惠券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/7/30 17:43:23

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

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

2026/7/30 17:43:23

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

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

2026/7/30 17:43:23

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

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

2026/7/30 18:33:27

巴中优壹加装饰坚守品质:自有施工队伍 全程工程零外包

摘要:巴中装修市场存在转包现象,可能导致工艺不稳定、工期拖延、售后责任不清等问题。本文对比本地自有施工团队的企业、连锁品牌与游击队的差异,分析零外包施工模式在工艺标准化、工期控制、报价透明、售后保障等方面的特点,为巴…

2026/7/30 18:33:27

MidJourney-Wrapper源码解析:Salai.py中的Discord交互协议实现

MidJourney-Wrapper源码解析:Salai.py中的Discord交互协议实现 【免费下载链接】MidJourney-Wrapper MidJourney wrapper in Discord. 项目地址: https://gitcode.com/gh_mirrors/mi/MidJourney-Wrapper MidJourney-Wrapper是一个基于Discord协议的MidJourne…

2026/7/30 18:33:27

Android16修改全局桌面视图边框四直角显示为弧边圆角

在 dimens.xml 设置 rounded_corner_radius为16dp(默认0dp)当SystemUI 检测到这个半径 > 0时,会在屏幕四个角创建 ImageViewframeworks\base\core\res\res\values\dimens.xml加载以下两个 XML 作为 ImageView 的 src,通过旋转/…

2026/7/29 22:32:30

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

一、背景与测试方案 在实际项目交付中,PDF文件合并与版权保护水印的叠加是一个高频但容易被低估的技术需求。典型的处理链路涉及:多源PDF的文件流合并、页面级水印渲染(含透明度混合与图层叠加)、输出文件体积控制。看似简单的操作…

2026/7/30 0:01:39

[GESP202606 四级] 扫雷

B4557 [GESP202606 四级] 扫雷 https://www.luogu.com.cn/problem/B4557 中国计算机学会(CCF)2026年6月C四级讲解——扫雷 https://www.bilibili.com/video/BV1MCMg6AEXR/ B4557 [GESP202606 四级] 扫雷 https://www.bilibili.com/video/BV1ZKTj6ZEVh/ 2…

2026/7/30 0:01:39

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼?是否遇到过设…

2026/7/29 13:12:43

3个高效策略:快速掌握Axure中文界面配置

3个高效策略:快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…