暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Redis分布式锁解决超卖问题

欢乐毅城 2021-07-24
487

注:每天进步一点点,记录每日成长,思考的第04/365天 ;  

本文预计阅读时间: 5分钟


Redis分布式锁解决电商订单库存并发问题。


基于Redis使用分布式锁已经不是什么新鲜事了。这篇文章分享的是使用redis分布式锁解决商品超卖事故的一个案例。


库存超卖问题是有很多种技术解决方案的,比如悲观锁分布式锁乐观锁队列串行化Redis原子操作,等等解决方案。


常见分布式锁解决方案:



三种分布式锁实现方式对比:

1、数据库分布式锁实现缺点
1)db操作性能较差,且有锁表的风险;
2)非阻塞操作失败后,需要轮询,占用cpu资源;
3)长时间不commit或者长时间轮询,可能会占用较多连接资源。


2、ZK分布式锁实现缺点
性能不如redis,因为其写操作(获取锁释放锁)都需要在Leader上执行,然后同步到follower。


3、Redis(缓存)分布式锁实现缺点
1)过期时间不好控制;
2)非阻塞,操作失败后,需要轮询,占用cpu资源;


1


01


使用原生redis实现

使用原生Redis
SetNX+
Expire实现的分布式锁

    //方案1 setnx
String lockKey = "zhyRedis";
   //通过val,给锁设置唯一id,防止其他线程删除锁
String clientId = UUID.randomUUID().toString();  //或者雪花生成位置ID
boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS); 
  // redisTemplate.expire(lockKey,10,TimeUnit.SECONDS) //旧版本redis(新版直接设置在后面,如上)
  //如果获取不到锁,则返回失败
if(!result){
    result "failed";
}
try {
 //如果能获取到锁,则返回成功
 Integer count = Integer.parseInt(redisTemplate.opsForValue.get("count").toString());
 if (count > 0) {
  Integer realCount = count - 1;
  System.out.Println("购买成功,剩余库存:" + realCount.toString());
  redisTemplate.opsForValue().set("count",realCount.toString());
 }else{
  System.out.Println("购买失败,库存不足");
 }
} catch(Exception e){
 e.printStackTrace();
  return "failed";
} finally{
  //解锁
  //判断当前客户端id与redis分布式中持有的客户端id一致,才能删除锁
 if(clientId.equals(redisTemplate.opsForValue().get(lockKey))){
   redisTemplate.delete(lockKey)
 }
}

复制

1


02


使用redisson实现

相较于原生Redis
SetNX+Expire
实现的分布式锁而言,Redisson
的分布式锁组件可以解决原生Redis
组合命令带来的一些缺陷,即redis的超时时间值不知道设置为多少才合适。如果此时Redis
的服务器节点恰好出现宕机或者服务不能用的情况,那将会导致相应的Key
永远存于缓存中,即处于所谓的“永久被锁死”的状态!

底层的实现机制在于:Redisson
内部提供了一个监控锁的看门狗WatchDog
,其作用在于Redis
实例被关闭之前,不断延长锁的有效期。

除此之外,Redisson
还通过加锁的方法提供了leaseTime
等参数来指定加锁的有效时间,即超过这个时间后“锁”便自动解开了。

//方案2 redisson
String lockKey = "zhyRedis";
RLock rLock = redisson.getLock(lockKey);
try {
 rLock.lock(10, TimeUnit.SECONDS)
 //如果能获取到锁,则返回成功
 Integer count = Integer.parseInt(redisTemplate.opsForValue.get("count").toString());
 if (count > 0) {
  Integer realCount = count - 1;
  System.out.Println("购买成功,剩余库存:" + realCount.toString());
  redisTemplate.opsForValue().set("count",realCount.toString());
 }else{
  System.out.Println("购买失败,库存不足");
 }
} catch(Exception e){
 e.printStackTrace();
 return "failed";
} finally{
 //解锁
 rLock.unlock();
}


复制


文章转载自欢乐毅城,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论