互联网大厂后端必看!Spring Boot 如何实现高并发抢券逻辑?

360影视 动漫周边 2025-05-12 14:57 2

摘要:在当今电商、本地生活服务等行业的各类促销活动里,高并发抢券的场景极为常见。就拿双 11、618 购物节来说,平台发放的优惠券数量有限,然而参与抢券的用户可能达到百万甚至千万级别,瞬间产生的高并发请求,对系统的稳定性和性能而言,无疑是巨大的挑战。Spring B

在当今电商、本地生活服务等行业的各类促销活动里,高并发抢券的场景极为常见。就拿双 11、618 购物节来说,平台发放的优惠券数量有限,然而参与抢券的用户可能达到百万甚至千万级别,瞬间产生的高并发请求,对系统的稳定性和性能而言,无疑是巨大的挑战。Spring Boot 作为当下后端开发中广泛运用的框架,凭借快速开发、简化配置等优势,成为实现高并发抢券逻辑的热门之选。不过,要想在高并发场景中充分发挥其最大效能,还需深入研究并合理运用各种技术手段。

分布式锁是解决高并发抢券问题的常用策略之一,借助 Redis 实现的具体步骤如下:

引入 Redis 依赖:在 Spring Boot 项目的 pom.xml 文件里添加 Redis 相关依赖,比如:

org.springframework.bootspring-boot-starter-data-redis

配置 Redis 连接:在 application.properties 或 application.yml 文件中设置 Redis 服务器的地址、端口等信息。以 application.yml 为例:

spring:redis:host: your - redis - hostport: 6379password: your - redis - password

编写获取锁的代码:利用 Redis 的 SET 命令结合 NX 和 EX 参数来尝试获取锁。

public boolean tryGetLock(RedisTemplate redisTemplate, String lockKey, String uniqueValue, int expireTime) {return redisTemplate.execute((RedisCallback) 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 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 的一个数值型 Key。可以通过 Redis 客户端工具,或者在 Spring Boot 代码中进行设置。假设 couponStockKey 是代表优惠券库存的 Key,stock 是初始库存数量,代码如下:

redisTemplate.opsForValue.set(couponStockKey, String.valueOf(stock));

编写抢券扣减库存代码:使用 Redis 的 decr 命令进行库存扣减。在 Spring Boot 中,通过 RedisTemplate 执行 decr 操作的代码如下:

public boolean tryRobCoupon(RedisTemplate redisTemplate, String couponStockKey) {return redisTemplate.execute((RedisCallback) connection -> {JedisCommands commands = (JedisCommands) connection.getNativeConnection;Long result = commands.decr(couponStockKey);return result >= 0;});}

该方法执行 decr 操作后,如果返回值大于等于 0,说明扣减成功,有券可抢;如果返回值小于 0,说明库存已耗尽。

处理可能的误扣情况:由于多线程并发执行 decr 可能出现库存扣成负数的现象,所以在扣减前可以先通过 GET 命令查询库存,判断是否还有剩余。同时,如果出现误扣,可以通过 incr 命令进行库存回滚。

public boolean checkAndRobCoupon(RedisTemplate 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 脚本:将抢券的核心逻辑封装在 Lua 脚本中,下面是一个实现先查询库存再扣减,并添加用户限制逻辑的 Lua 脚本示例:

local stockKey = KEYS[1]local userSetKey = ARGV[1]local userId = ARGV[2]local stock = tonumber(redis.call('GET', stockKey))if stock

这个脚本中,KEYS [1] 代表优惠券库存的 Key,ARGV [1] 是存储已抢券用户的 Set 集合的 Key,ARGV [2] 是当前抢券用户的标识。脚本首先查询库存,如果库存不足返回 0;然后检查用户是否已经抢过券,如果已抢过返回 - 1;否则扣减库存并将用户标识加入已抢券用户集合,最后返回 1 表示抢券成功。

在 Spring Boot 中执行 Lua 脚本:使用 RedisTemplate 执行 Lua 脚本。通过 RedisTemplate 的 execute 方法,传入 Lua 脚本和相关参数。

public int executeLuaScript(RedisTemplate 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 redisScript = new DefaultRedisScript(luaScript, Integer.class);List keys = Collections.singletonList(stockKey);List args = Arrays.asList(userSetKey, userId);return redisTemplate.execute(redisScript, keys, args);}

在实际的互联网大厂项目中,实现高并发抢券功能,除了要关注上述核心技术方案外,还需要考虑系统的整体架构设计、性能优化、容灾备份等多个方面。例如,可以采用负载均衡技术将请求均匀分配到多个后端服务器,使用缓存来减轻数据库压力,设计合理的降级和熔断策略以应对突发流量高峰等。希望本文介绍的 Spring Boot 结合 Redis 实现高并发抢券的方法,能为各位互联网大厂后端开发人员在实际项目中提供有效的参考和帮助。让我们一起在高并发的技术浪潮中,打造出更加稳定、高效的系统。

来源:从程序员到架构师一点号

相关推荐