做Java后端开发,只要涉及秒杀、限量库存、下单扣减业务,订单超卖绝对是绕不开的经典并发问题。
我之前接手过一个电商项目,压测的时候发现严重漏洞:商品库存明明只剩10个,高并发下单后,最终成交订单达到了15个,直接超卖5单,导致后台对账出错、商家亏损,线上事故非常严重。
最开始我用 synchronized、ReentrantLock 本地锁解决,本地锁在单体项目没问题,但项目集群部署、多实例之后,锁直接失效。因为本地锁只针对当前JVM生效,不同服务实例之间无法互斥,并发冲突依旧存在。
最终在生产环境引入 Redisson 分布式锁 彻底根治超卖问题。今天结合真实线上实战,完整复盘从问题出现、原理分析到代码落地的全套流程,新手也能直接看懂、直接复用上线。

一、为什么会出现订单超卖?
我简单还原一下业务场景:用户下单前,程序先查询库存,判断库存大于0,就执行扣减库存、生成订单逻辑。
在高并发场景下,大量请求同时查询库存,此时库存还没来得及扣减,所有请求都判定库存充足,同时进入下单逻辑,就会出现库存扣减错乱、超卖溢出。
很多新手误区:觉得加个事务、判断一下库存就能解决并发问题。实际上数据库事务只能保证数据一致性,无法解决并发争抢问题。集群部署下,普通锁全部失效,这也是为什么必须用分布式锁的核心原因。
二、Redisson 核心优势(为什么不用原生Redis锁)
网上很多教程用 Redis 的 SETNX 手写分布式锁,我非常不推荐。手写锁坑非常多:锁超时释放、锁误删、主从异步复制丢锁、无法续期等问题,线上极易出事故。
而 Redisson 是官方推荐的企业级分布式锁框架,完美解决了以上所有问题:自带看门狗续期机制、可重入锁、公平锁、读写锁,适配集群、哨兵、单机各种Redis架构,稳定性拉满,是目前生产环境最优解。
三、项目依赖与环境配置
首先引入Redisson核心依赖,适配SpringBoot3.x,Maven依赖如下:
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.23.5</version> </dependency>
yml配置文件,直接连接本地或线上Redis:
spring: redis: host: 127.0.0.1 port: 6379 password: database: 0
四、Redisson分布式锁实战代码(解决超卖)
下面是我线上落地的完整业务代码,模拟库存扣减、下单流程,通过 Redisson 可重入锁保证并发互斥,彻底杜绝超卖。
import org.redisson.RLock; import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; @RestController public class OrderController { @Autowired private RedissonClient redissonClient; @Autowired private RedisTemplate<String, String> redisTemplate; // 库存锁Key,保证同一商品唯一锁 private static final String STOCK_LOCK_KEY = "lock:stock:001"; @GetMapping("/create/order") public String createOrder(){ // 获取分布式可重入锁 RLock lock = redissonClient.getLock(STOCK_LOCK_KEY); try { // 尝试加锁,等待3秒,锁自动释放时间30秒 boolean tryLock = lock.tryLock(3, 30, TimeUnit.SECONDS); if (!tryLock){ return "系统繁忙,请稍后重试"; } // 查询库存 String stockStr = redisTemplate.opsForValue().get("goods:stock:001"); int stock = Integer.parseInt(stockStr); // 库存判断 if (stock <= 0){ return "商品库存不足"; } // 扣减库存 redisTemplate.opsForValue().decrement("goods:stock:001"); // 此处可拓展:生成订单、写入数据库、消息通知等业务逻辑 System.out.println("订单创建成功,剩余库存:" + (stock - 1)); return "下单成功"; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return "下单失败"; } finally { // 释放锁,必须放在finally,防止死锁 if (lock.isHeldByCurrentThread()){ lock.unlock(); } } } }
这段代码是生产通用标准写法,加锁、超时时间、锁释放逻辑齐全,杜绝死锁和
五、核心原理简单解读
很多人只会用不会原理,我简单通俗讲一下。
Redisson 的 tryLock 方法会通过Lua脚本保证加锁原子性,避免并发加锁冲突。同时内置看门狗机制,如果业务执行超时未释放锁,会自动续期,防止业务没执行完锁提前释放导致并发问题。
finally 释放锁是关键,必须判断当前线程持有锁才释放,防止别的线程误删锁,这是手写锁最容易踩的坑。
六、压测效果对比
我用JMeter压测对比非常明显:未加分布式锁前,1000并发请求直接超卖;接入Redisson分布式锁后,无论多高并发,库存精准扣减,完全杜绝超卖问题,数据一致性100%没问题。
七、生产落地避坑总结
第一,锁的Key一定要精准,按商品维度加锁,不能全局单锁,不然会导致接口性能雪崩;第二,务必设置超时时间,避免Redis宕机导致死锁;第三,禁止手动过期释放,依赖Redisson自动机制更安全。
总结
在分布式集群场景下,本地锁完全无法解决并发超卖问题,Redisson分布式锁是目前最简单、最稳定、零BUG的解决方案。本文代码可以直接用于秒杀、限量活动、库存扣减等所有并发场景,适配绝大多数SpringBoot电商项目,帮大家彻底解决线上订单超卖的头疼问题。