存在问题 存在问题 存在问题思路
步骤
Java 功能代码
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public boolean doSecondKill(String uid, String goodsId) {
// 1. 参数校验
if( StringUtils.isEmpty(uid) || StringUtils.isEmpty(goodsId) ) {
return false;
}
// 2. 获取Redis操作对象
ValueOperations<String, Object> strOps =redisTemplate.opsForValue();
SetOperations<String, Object> setOps = redisTemplate.opsForSet();
// 3. 拼接key
// 3.1 库存key
String stockKey = "SK:"+goodsId+":nums";
// 3.2 秒杀用户成功key
String userKey = "SK:" + goodsId + ":users";
// 4. 获取库存,如果库存为空,秒杀还未开始
Object stock = strOps.get(stockKey);
if(StringUtils.isEmpty(stock)) {
System.out.println("秒杀还未开始,请等待……");
return false;
}
// 5. 判断用户是否重复秒杀操作
if(setOps.isMember(userKey, uid)) {
System.out.println("已参与");
return false;
}
// 6. 判断商品数量,如果商品数量小于1,秒杀结束
if(Integer.parseInt(stock.toString()) < 1) {
System.out.println("秒杀已结束");
return false;
}
// 7. 秒杀
// 7.1 库存减一
strOps.decrement(stockKey);
// 7.2 秒杀成功的用户添加清单里面
setOps.add(userKey, uid);
return true;
}
解决超卖问题
public boolean doSecondKill(String uid, String goodsId) {
// 1. 参数校验
if( StringUtils.isEmpty(uid) || StringUtils.isEmpty(goodsId) ) {
return false;
}
// 2. 获取Redis操作对象
ValueOperations<String, Object> strOps =redisTemplate.opsForValue();
SetOperations<String, Object> setOps = redisTemplate.opsForSet();
// 3. 拼接key
// 3.1 库存key
String stockKey = "SK:"+goodsId+":nums";
// 3.2 秒杀用户成功key
String userKey = "SK:" + goodsId + ":users";
redisTemplate.watch(stockKey);
// 4. 获取库存,如果库存为空,秒杀还未开始
Object stock = strOps.get(stockKey);
if(StringUtils.isEmpty(stock)) {
System.out.println("秒杀还未开始,请等待……");
return false;
}
// 5. 判断用户是否重复秒杀操作
if(setOps.isMember(userKey, uid)) {
System.out.println("已参与");
return false;
}
// 6. 判断商品数量,如果商品数量小于1,秒杀结束
if(Integer.parseInt(stock.toString()) < 1) {
System.out.println("秒杀已结束");
return false;
}
// 7. 秒杀
redisTemplate.multi();
// 7.1 库存减一
strOps.decrement(stockKey);
// 7.2 秒杀成功的用户添加清单里面
setOps.add(userKey, uid);
List<Object> execs = redisTemplate.exec();
if(null == execs || execs.isEmpty()) {
System.out.println("秒杀失败");
return false;
}
return true;
}
解决库存遗留问题
private void lock(String lockKey, String lockValue, String uid) {
Boolean nativeLock = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, Duration.ofSeconds(30));
System.out.println(uid + "->" + "加锁状态:" + nativeLock);
if(nativeLock) { // 加锁成功
try {
// 业务代码
// ... 此处省略
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) else return 0 end";
Integer ret = redisTemplate.execute(new DefaultRedisScript<Integer>(script, Integer.class), Arrays.asList(lockKey), lockValue);
System.out.println(uid + "->" + "解锁:" + "\t\t" + ret);
}
} else { // 自旋锁
System.out.println(uid + "->" + "加锁失败,睡眠100ms ");
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock(lockKey, lockValue, uid);
}
}
解决超时问题
local stockKey = KEYS[1];
local userKey = KEYS[2];
local userId = ARGV[1];
-- 判断用户是否已参与
local userExists=redis.call("sismember", userKey, userId);
if type(userExists) == "number" and tonumber(userExists)==1 then
-- 已参与
return 2;
end
-- 库存判断 & 减库存操作
local num = redis.call("get", stockKey);
if type(num) == "boolean" then -- 还未开始
return -1;
elseif type(num) == "number" and tonumber(num) <= 0 then -- 秒杀结束
return 0;
else
redis.call("decr", stockKey);
redis.call("sadd", userKey, userId);
end
-- 秒杀成功
return 1;
private Boolean redissonLock(String lockName, String uid, String goodsId) {
RLock lock = redissonClient.getLock(lockName);
lock.lock();
try {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("redis/sk.lua")));
redisScript.setResultType(Long.class);
String stockKey = "SK:" + goodsId + ":stocks";
String userKey = "SK:" + goodsId + ":users";
// System.out.println(stockKey);
// System.out.println(userKey);
Long ret = redisTemplate.execute(redisScript, Arrays.asList(stockKey, userKey), uid);
System.out.println(ret);
return ret == 1;
} finally {
lock.unlock();
}
}