互联网大厂后端必看!Spring Boot 如何实现高并发抢券逻辑?
mhr18 2025-05-21 15:13 33 浏览 0 评论
在当今电商、本地生活服务等行业的各类促销活动里,高并发抢券的场景极为常见。就拿双 11、618 购物节来说,平台发放的优惠券数量有限,然而参与抢券的用户可能达到百万甚至千万级别,瞬间产生的高并发请求,对系统的稳定性和性能而言,无疑是巨大的挑战。Spring Boot 作为当下后端开发中广泛运用的框架,凭借快速开发、简化配置等优势,成为实现高并发抢券逻辑的热门之选。不过,要想在高并发场景中充分发挥其最大效能,还需深入研究并合理运用各种技术手段。
分布式锁方案
分布式锁是解决高并发抢券问题的常用策略之一,借助 Redis 实现的具体步骤如下:
引入 Redis 依赖:在 Spring Boot 项目的 pom.xml 文件里添加 Redis 相关依赖,比如:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置 Redis 连接:在 application.properties 或 application.yml 文件中设置 Redis 服务器的地址、端口等信息。以 application.yml 为例:
spring:
redis:
host: your - redis - host
port: 6379
password: your - redis - password
编写获取锁的代码:利用 Redis 的 SET 命令结合 NX 和 EX 参数来尝试获取锁。
public boolean tryGetLock(RedisTemplate<String, String> redisTemplate, String lockKey, String uniqueValue, int expireTime) {
return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
String result = commands.set(lockKey, uniqueValue, "NX", "EX", expireTime);
return "OK".equals(result);
});
}
这里 lockKey 是锁的唯一标识,uniqueValue 是每个请求的唯一值(比如可以用 UUID 生成),用于防止误删锁,expireTime 是锁的过期时间,单位为秒。
执行业务逻辑并释放锁:当成功获取到锁后,执行优惠券库存扣减、订单生成等核心业务逻辑。完成后,通过 DEL 命令释放锁。
public void releaseLock(RedisTemplate<String, String> redisTemplate, String lockKey, String uniqueValue) {
String script = "if redis.call('GET', KEYS(1)) == ARGV(1) then return redis.call('DEL', KEYS(1)) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Collections.singletonList(lockKey), uniqueValue);
}
这里运用 Lua 脚本确保判断锁的持有者和释放锁的操作具有原子性,避免在高并发下出现误释放其他线程锁的情况。
这种方式能够保证同一时间仅有一个线程或请求可以执行关键业务逻辑,有效防止超卖现象的发生。但它也存在一些问题,例如在高并发时,大量请求争抢锁会形成性能瓶颈,而且要是锁的过期时间设置不合理,还可能出现锁提前释放导致的并发问题。
利用 Redis 原子操作特性方案
利用 Redis 的原子操作特性实现高并发抢券的具体步骤如下:
初始化库存到 Redis:在活动开始前,将优惠券库存数量设置为 Redis 的一个数值型 Key。可以通过 Redis 客户端工具,或者在 Spring Boot 代码中进行设置。假设 couponStockKey 是代表优惠券库存的 Key,stock 是初始库存数量,代码如下:
redisTemplate.opsForValue().set(couponStockKey, String.valueOf(stock));
编写抢券扣减库存代码:使用 Redis 的 decr 命令进行库存扣减。在 Spring Boot 中,通过 RedisTemplate 执行 decr 操作的代码如下:
public boolean tryRobCoupon(RedisTemplate<String, String> redisTemplate, String couponStockKey) {
return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
Long result = commands.decr(couponStockKey);
return result >= 0;
});
}
该方法执行 decr 操作后,如果返回值大于等于 0,说明扣减成功,有券可抢;如果返回值小于 0,说明库存已耗尽。
处理可能的误扣情况:由于多线程并发执行 decr 可能出现库存扣成负数的现象,所以在扣减前可以先通过 GET 命令查询库存,判断是否还有剩余。同时,如果出现误扣,可以通过 incr 命令进行库存回滚。
public boolean checkAndRobCoupon(RedisTemplate<String, String> redisTemplate, String couponStockKey) {
String stockStr = redisTemplate.opsForValue().get(couponStockKey);
if (stockStr == null) {
return false;
}
int stock = Integer.parseInt(stockStr);
if (stock > 0) {
boolean success = tryRobCoupon(redisTemplate, couponStockKey);
if (!success) {
// 出现误扣,进行回滚
redisTemplate.opsForValue().increment(couponStockKey);
}
return success;
}
return false;
}
结合 Lua 脚本和 Redis 原子操作方案
结合 Lua 脚本和 Redis 的原子操作实现抢券逻辑的具体步骤如下:
编写 Lua 脚本:将抢券的核心逻辑封装在 Lua 脚本中,下面是一个实现先查询库存再扣减,并添加用户限制逻辑的 Lua 脚本示例:
local stockKey = KEYS[1]
local userSetKey = ARGV[1]
local userId = ARGV[2]
local stock = tonumber(redis.call('GET', stockKey))
if stock <= 0 then
return 0
end
local isRobbed = redis.call('SISMEMBER', userSetKey, userId)
if isRobbed == 1 then
return -1
end
redis.call('DECR', stockKey)
redis.call('SADD', userSetKey, userId)
return 1
这个脚本中,KEYS [1] 代表优惠券库存的 Key,ARGV [1] 是存储已抢券用户的 Set 集合的 Key,ARGV [2] 是当前抢券用户的标识。脚本首先查询库存,如果库存不足返回 0;然后检查用户是否已经抢过券,如果已抢过返回 - 1;否则扣减库存并将用户标识加入已抢券用户集合,最后返回 1 表示抢券成功。
在 Spring Boot 中执行 Lua 脚本:使用 RedisTemplate 执行 Lua 脚本。通过 RedisTemplate 的 execute 方法,传入 Lua 脚本和相关参数。
public int executeLuaScript(RedisTemplate<String, String> redisTemplate, String stockKey, String userSetKey, String userId) {
String luaScript = "local stockKey = KEYS[1]\n" +
"local userSetKey = ARGV[1]\n" +
"local userId = ARGV[2]\n" +
"\n" +
"local stock = tonumber(redis.call('GET', stockKey))\n" +
"if stock <= 0 then\n" +
" return 0\n" +
"end\n" +
"\n" +
"local isRobbed = redis.call('SISMEMBER', userSetKey, userId)\n" +
"if isRobbed == 1 then\n" +
" return -1\n" +
"end\n" +
"\n" +
"redis.call('DECR', stockKey)\n" +
"redis.call('SADD', userSetKey, userId)\n" +
"return 1";
DefaultRedisScript<Integer> redisScript = new DefaultRedisScript<>(luaScript, Integer.class);
List<String> keys = Collections.singletonList(stockKey);
List<String> args = Arrays.asList(userSetKey, userId);
return redisTemplate.execute(redisScript, keys, args);
}
在实际的互联网大厂项目中,实现高并发抢券功能,除了要关注上述核心技术方案外,还需要考虑系统的整体架构设计、性能优化、容灾备份等多个方面。例如,可以采用负载均衡技术将请求均匀分配到多个后端服务器,使用缓存来减轻数据库压力,设计合理的降级和熔断策略以应对突发流量高峰等。希望本文介绍的 Spring Boot 结合 Redis 实现高并发抢券的方法,能为各位互联网大厂后端开发人员在实际项目中提供有效的参考和帮助。让我们一起在高并发的技术浪潮中,打造出更加稳定、高效的系统。
相关推荐
- Dubai's AI Boom Lures Global Tech as Emirate Reinvents Itself as Middle East's Silicon Gateway
-
AI-generatedimageAsianFin--Dubaiisrapidlytransformingitselffromadesertoilhubintoaglob...
- OpenAI Releases o3-pro, Cuts o3 Prices by 80% as Deal with Google Cloud Reported to Make for Compute Needs
-
TMTPOST--OpenAIisescalatingthepricewarinlargelanguagemodel(LLM)whileseekingpartnershi...
- 黄仁勋说AI Agent才是未来!但究竟有些啥影响?
-
,抓住风口(iOS用户请用电脑端打开小程序)本期要点:详解2025年大热点你好,我是王煜全,这里是王煜全要闻评论。最近,有个词被各个科技大佬反复提及——AIAgent,智能体。黄仁勋在CES展的发布...
- 商城微服务项目组件搭建(五)——Kafka、Tomcat等安装部署
-
1、本文属于mini商城系列文档的第0章,由于篇幅原因,这篇文章拆成了6部分,本文属于第5部分2、mini商城项目详细文档及代码见CSDN:https://blog.csdn.net/Eclipse_...
- Python+Appium环境搭建与自动化教程
-
以下是保姆级教程,手把手教你搭建Python+Appium环境并实现简单的APP自动化测试:一、环境搭建(Windows系统)1.安装Python访问Python官网下载最新版(建议...
- 零配置入门:用VSCode写Java代码的正确姿
-
一、环境准备:安装JDK,让电脑“听懂”Java目标:安装Java开发工具包(JDK),配置环境变量下载JDKJava程序需要JDK(JavaDevelopmentKit)才能运行和编译。以下是两...
- Mycat的搭建以及配置与启动(mycat2)
-
1、首先开启服务器相关端口firewall-cmd--permanent--add-port=9066/tcpfirewall-cmd--permanent--add-port=80...
- kubernetes 部署mysql应用(k8s mysql部署)
-
这边仅用于测试环境,一般生产环境mysql不建议使用容器部署。这里假设安装mysql版本为mysql8.0.33一、创建MySQL配置(ConfigMap)#mysql-config.yaml...
- Spring Data Jpa 介绍和详细入门案例搭建
-
1.SpringDataJPA的概念在介绍SpringDataJPA的时候,我们首先认识下Hibernate。Hibernate是数据访问解决技术的绝对霸主,使用O/R映射(Object-Re...
- 量子点格棋上线!“天衍”邀您执子入局
-
你是否能在策略上战胜量子智能?这不仅是一场博弈更是一次量子智力的较量——量子点格棋正式上线!试试你能否赢下这场量子智局!游戏玩法详解一笔一画间的策略博弈游戏目标:封闭格子、争夺领地点格棋的基本目标是利...
- 美国将与阿联酋合作建立海外最大的人工智能数据中心
-
当地时间5月15日,美国白宫宣布与阿联酋合作建立人工智能数据中心园区,据称这是美国以外最大的人工智能园区。阿布扎比政府支持的阿联酋公司G42及多家美国公司将在阿布扎比合作建造容量为5GW的数据中心,占...
- 盘后股价大涨近8%!甲骨文的业绩及指引超预期?
-
近期,美股的AI概念股迎来了一波上升行情,微软(MSFT.US)频创新高,英伟达(NVDA.US)、台积电(TSM.US)、博通(AVGO.US)、甲骨文(ORCL.US)等多股亦出现显著上涨。而从基...
- 甲骨文预计新财年云基础设施营收将涨超70%,盘后一度涨8% | 财报见闻
-
甲骨文(Oracle)周三盘后公布财报显示,该公司第四财季业绩超预期,虽然云基建略微逊于预期,但管理层预计2026财年云基础设施营收预计将增长超过70%,同时资本支出继上年猛增三倍后,新财年将继续增至...
- Springboot数据访问(整合MongoDB)
-
SpringBoot整合MongoDB基本概念MongoDB与我们之前熟知的关系型数据库(MySQL、Oracle)不同,MongoDB是一个文档数据库,它具有所需的可伸缩性和灵活性,以及所需的查询和...
- Linux环境下,Jmeter压力测试的搭建及报错解决方法
-
概述 Jmeter最早是为了测试Tomcat的前身JServ的执行效率而诞生的。到目前为止,它的最新版本是5.3,其测试能力也不再仅仅只局限于对于Web服务器的测试,而是涵盖了数据库、JM...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- Dubai's AI Boom Lures Global Tech as Emirate Reinvents Itself as Middle East's Silicon Gateway
- OpenAI Releases o3-pro, Cuts o3 Prices by 80% as Deal with Google Cloud Reported to Make for Compute Needs
- 黄仁勋说AI Agent才是未来!但究竟有些啥影响?
- 商城微服务项目组件搭建(五)——Kafka、Tomcat等安装部署
- Python+Appium环境搭建与自动化教程
- 零配置入门:用VSCode写Java代码的正确姿
- Mycat的搭建以及配置与启动(mycat2)
- kubernetes 部署mysql应用(k8s mysql部署)
- Spring Data Jpa 介绍和详细入门案例搭建
- 量子点格棋上线!“天衍”邀您执子入局
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle 空为0 (51)
- oracle主从同步 (56)
- oracle 乐观锁 (53)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)