欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

畅购商城项目改造--redis分布式锁解决缓存击穿的问题

程序员文章站 2022-05-04 12:52:01
...

分布式锁主流的实现方案:

  1. 基于数据库实现分布式锁
  2. 基于缓存(Redis等)
  3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

  1. 性能:redis最高

  2. 可靠性:zookeeper最高
    但是畅购项目并没有使用zookeeper进行开发,功能由Eureka接替,我也没有在项目实现改造。下一篇文章再说利用模板设计模式实现分布式锁的问题。
    借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同时有多个客户端发送setnx命令,只有一个客户端可以成功,返回1(true);其他的客户端返回0(false)。
    畅购商城项目改造--redis分布式锁解决缓存击穿的问题

  3. 多个客户端同时获取锁(setnx)

  4. 获取成功,执行业务逻辑,执行完成释放锁(del)

  5. 其他客户端等待重试
    基本思想就是这样改造,为了解决 缓存击穿 击穿是一个热点key失效,执行redis的setnx命令
    uuid是保证给自己的锁唯一标志,防止误删。
    同时string script是lua脚本的执行命令,保证原子性。

     String uuid = UUID.randomUUID().toString();
     Boolean aBoolean = this.redisTemplate.opsForValue().setIfAbsent("lock", uuid,10,TimeUnit.SECONDS);
    
     Content content=new Content();
     //判断是否拿到锁
     if(aBoolean){
         String jsonContent = redisTemplate.boundValueOps("content_" + id).get();
         if(jsonContent!=null){
             List<Content> contents;
             contents = JSON.parseObject(jsonContent, List.class);
             return contents;
         }
         content.setCategoryId(id);
         content.setStatus("1");
    
    
         // 2. 释放锁 del
         String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
         this.redisTemplate.execute(new DefaultRedisScript<>(script), Arrays.asList("lock"), Arrays.asList(uuid));
    
     }else {
         //其他请求尝试获取锁
         findByCategory(id);
     }
    
     return contentMapper.select(content);
    

备注:其实最好的方式还是redission加上AOP环绕通知,做成注解实现,具体查阅google