后端与系统架构1452 字
商品秒杀巧用Redis与Lua预防库存超卖~
·全库 389 / 611
《商品秒杀巧用Redis与Lua预防库存超卖~》共 0.1 万字,通篇围绕 Redis、并发、场景、数据库 展开,归在「后端与系统架构」这一层。
覆盖Redis并发场景数据库上下文安全
适合给 AI 系统搭底座、要管得住的人
目录骨架(4 节)
- 1. 原理与执行方式
- 2. 性能与效率
- 3. 适用场景
- Lua脚本
方案分析
在高并发秒杀场景下,库存超卖的主要原因是:
- 多个并发请求同时操作库存,导致库存变负或超卖。
- 数据库性能瓶颈无法快速响应大量请求。
Redis+Lua解决方案
Redis是单线程的,结合Lua脚本可以保证对库存的操作是原子的,避免并发问题。
- Redis的优势:高并发、高性能、支持丰富的数据结构。
- Lua的优势:运行在Redis中,可以在一个原子操作中完成多条命令。
核心思路
- 商品秒杀前,将商品的库存预先加载到Redis中。
- 使用Lua脚本对库存进行扣减操作,确保每个请求的库存操作是原子的。
- Lua脚本校验库存是否足够,如果足够则扣减库存并返回秒杀成功;否则返回秒杀失败。
Lua脚本实现库存扣减
lua
-- Lua脚本:扣减库存
local stock_key = KEYS[1] -- 商品库存Key
local user_key = KEYS[2] -- 用户秒杀Key,防止重复秒杀
local stock = tonumber(redis.call('get', stock_key))
-- 判断库存是否足够
if stock <= 0 then
return -1 -- 库存不足
end
-- 判断用户是否已经秒杀过
if redis.call('sismember', user_key, ARGV[1]) == 1 then
return 0 -- 用户已经秒杀
end
-- 扣减库存
redis.call('decr', stock_key)
-- 记录用户已经秒杀
redis.call('sadd', user_key, ARGV[1])
return 1 -- 秒杀成功
Java代码调用Lua脚本
在Java中使用Jedis,Redis客户端调用上述Lua脚本。下面是一个基于Jedis的示例代码:
java
复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class SecKillService {
private JedisPool jedisPool;
public SecKillService(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
public boolean doSecKill(String userId, String productId) {
try (Jedis jedis = jedisPool.getResource()) {
// Lua脚本
String luaScript = "local stock_key = KEYS[1];" +
"local user_key = KEYS[2];" +
"local stock = tonumber(redis.call('get', stock_key));" +
"if stock <= 0 then return -1 end;" +
"if redis.call('sismember', user_key, ARGV[1]) == 1 then return 0 end;" +
"redis.call('decr', stock_key);" +
"redis.call('sadd', user_key, ARGV[1]);" +
"return 1;";
String stockKey = "product_stock_" + productId;
String userKey = "user_seckill_" + productId;
// 执行Lua脚本
Object result = jedis.eval(luaScript, 2, stockKey, userKey, userId);
if (result.equals(1L)) {
System.out.println("秒杀成功");
return true;
} else if (result.equals(0L)) {
System.out.println("你已经秒杀过了");
return false;
} else {
System.out.println("库存不足");
return false;
}
}
}
}
使用场景示例
假设我们有一个热销商品的秒杀活动,活动开始时将商品的库存加载到Redis中:
java
public void preloadStock(String productId, int stock) {
try (Jedis jedis = jedisPool.getResource()) {
String stockKey = "product_stock_" + productId;
jedis.set(stockKey, String.valueOf(stock)); // 设置库存
}
}
在秒杀开始时,用户请求会通过Java代码调用Lua脚本进行库存的校验与扣减操作,保证每个操作是线程安全的。
秒杀并发防超卖架构图

