Redis库存自减逻辑怎么实现?高并发场景下怎么保证数据一致性?

文章导读
使用Redis的原子操作decr命令实现库存自减,代码如下:if (redisClient.decr("stock:" + productId) < 0) { redisClient.incr("stock:" + productId); // 回滚 return false;}return true;高并发下用Lua脚本保证原子性:local stock = redis.cal
📋 目录
  1. 方案一:Lua脚本原子扣减
  2. 分布式锁保障一致性
  3. 方案二:乐观锁 + WATCH
  4. 超卖解决方案
  5. 真实案例代码
  6. 性能优化
A A

使用Redis的原子操作decr命令实现库存自减,代码如下:
if (redisClient.decr("stock:" + productId) < 0) {
redisClient.incr("stock:" + productId); // 回滚
return false;
}
return true;
高并发下用Lua脚本保证原子性:
local stock = redis.call('get', KEYS[1])
if tonumber(stock) > 0 then
redis.call('decr', KEYS[1])
return 1
else
return 0
end

方案一:Lua脚本原子扣减

在高并发场景下,为了保证库存扣减的原子性,我们可以使用Redis的Lua脚本。Lua脚本在Redis中是原子执行的,可以避免并发问题。
-- Lua脚本内容
local stock_key = KEYS[1]
local num = tonumber(ARGV[1])
local stock = tonumber(redis.call('get', stock_key) or 0)
if stock >= num then
redis.call('decrby', stock_key, num)
return 1
end
return 0

分布式锁保障一致性

使用Redisson分布式锁:
RLock lock = redisson.getLock("lock:" + productId);
try {
lock.lock(10, TimeUnit.SECONDS);
Long stock = redisTemplate.opsForValue().decrement("stock:" + productId);
if (stock != null && stock >= 0) {
// 下单成功
} else {
redisTemplate.opsForValue().increment("stock:" + productId);
}
} finally {
lock.unlock();
}

Redis库存自减逻辑怎么实现?高并发场景下怎么保证数据一致性?

方案二:乐观锁 + WATCH

Redis的WATCH命令实现乐观锁:
MULTI
WATCH stock:123
DECR stock:123
EXEC
在高并发下,如果EXEC失败(因为WATCH的key被修改),则重试。这样保证了扣减的原子性和一致性。

超卖解决方案

为了防止超卖,除了原子扣减,还需要结合数据库事务:
1. Redis预减库存
2. 数据库扣减库存 + 记录订单
3. 如果Redis扣减失败,回滚数据库
4. 异步补偿:定时任务检查Redis和DB库存差异,进行修正。

真实案例代码

完整的SpringBoot实现:
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean deductStock(String productId, int quantity) {
String key = "stock:" + productId;
Long result = redisTemplate.opsForValue().decrement(key, quantity);
if (result == null || result < 0) {
if (result != null) {
redisTemplate.opsForValue().increment(key, quantity);
}
return false;
}
return true;
}

Redis库存自减逻辑怎么实现?高并发场景下怎么保证数据一致性?

性能优化

高并发场景下,单机Redis可能成为瓶颈,建议使用Redis Cluster集群,并设置合理的过期时间,避免内存爆炸。结合MQ异步化非核心扣减逻辑。

FAQ
Q: Redis dectr扣减后小于0怎么办?
A: 立即incr回滚,并返回扣减失败。

Q: Lua脚本为什么比分布式锁快?
A: Lua脚本单线程原子执行,无网络往返,性能更高。

Q: 怎么处理Redis宕机?
A: 双写Redis和DB,Redis只做缓存,DB做最终一致性。

Q: 秒杀场景怎么防刷?
A: 加验证码、限流、黑名单,用Lua脚本判断用户是否重复下单。