高并发场景下秒杀下单超卖问题
模拟扣减库存方法(JVM级别锁,分布式环境下异常)
@RequestMapping("/deduct_stock")
public String deductStock(){
synchronized (this) {
int stock=Integer.parseInt((stringRedisTemplate.opsForValue().get("stock")));//jedis.get("stock");
if(stock>0){
int realStock=stock-1;
stringRedisTemplate.opsForValue().set("stock",realStock+"");//jedis.set(key,value);
System.out.println("扣减成功,剩余库存:"+realStock);
}else{
System.out.println("扣减失败,库存不足");
}
}
return "end";
}
1.使用redis的setnx实现分布式锁
@RequestMapping("/deduct_stock")
public String deductStock(){
String lockKey="product_101"
Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zz");
if(!result) {
return "error_code";
}
try {
int stock = Integer.parseInt((stringRedisTemplate.opsForValue().get("stock")));//jedis.get("stock");
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");//jedis.set(key,value);
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
}finally {
stringRedisTemplate.delete(lockKey);//锁释放
}
return "end";
}
问题:系统宕机finall未执行,则锁未释放
解决办法:设置超时时间
stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
问题:设置完锁突然异常,未成功设置超时时间
解决办法:原子操作设置锁和超时时间
Boolean result=stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"zz",10, TimeUnit.SECONDS);
问题:线程2删除了线程1的锁,锁失效
解决办法:只能删除clientId自身的锁
问题:锁判断、删除也需要原子执行
解决办法:锁续命