redis分布式锁防秒杀超卖的核心是保证“查库存+扣库存”原子性;单机synchronized无法跨jvm,数据库行锁高并发性能差;需用setnx+随机token加锁,并通过lua脚本原子删锁,或直接使用redisson实现可重入、自动续期的健壮锁。

用 Redis 分布式锁防秒杀超卖,核心是让“查库存 + 扣库存”变成一个不可分割的动作,确保同一商品同一时刻只被一个请求处理。
为什么单靠数据库或 synchronized 不行
数据库行锁在高并发下性能扛不住,synchronized 只在单 JVM 内生效——集群部署后,不同服务器上的线程互不感知,照样会同时读到库存 > 0,然后一起扣减,导致超卖。分布式锁就是为跨机器的并发互斥而生的。
用 SETNX + 随机 Token 实现基础锁
Redis 的 SET key value NX EX seconds 是最常用的加锁命令:
- NX 保证只有 key 不存在时才设置成功,天然实现“抢锁”逻辑
- EX 设置自动过期时间(比如 10 秒),避免业务异常卡住导致死锁
- value 必须是唯一随机标识(如 UUID),后续删锁时用来校验,防止误删别人持有的锁
Java 示例(StringRedisTemplate):
boolean locked = stringRedisTemplate.opsForValue().setIfAbsent("lock:goods:123", "uuid-abc", 10, TimeUnit.SECONDS);
拿到锁后,再执行库存判断与扣减;完成后,必须先校验 value 是否匹配,再删除 key——这个校验+删除不能拆成两步,否则有竞态风险。
用 Lua 脚本保证删锁原子性
删锁操作(GET + DEL 判断)若分两步,在高并发下可能刚读到自己的 token,还没来得及 DEL,锁就过期了,另一个线程加锁成功,此时原线程 DEL 就会删掉别人的锁。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
解决方案:用 Lua 脚本把“判断 value 是否匹配 + 删除”封装成一个原子操作:
redis.eval("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", Arrays.asList("lock:goods:123"), Arrays.asList("uuid-abc"));
Redis 单线程执行 Lua,整个过程不会被打断,彻底规避误删。
更稳的选择:直接用 Redisson
自己手写锁容易踩坑(锁续期、可重入、看门狗机制、公平性等)。Redisson 封装了成熟的分布式可重入锁,一行代码就能安全加锁:
RLock lock = redissonClient.getLock("lock:goods:123");
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
try {
// 执行扣库存、下订单等业务
} finally {
lock.unlock(); // 自动释放,支持看门狗续期
}
}
它内部自动维护锁的续期、防误删、可重入,并且 unlock 是幂等的,即使重复调用也不会报错。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










