美文网首页
Redis分布式锁

Redis分布式锁

作者: zouhao1985 | 来源:发表于2020-07-14 21:36 被阅读0次

    1.Redis分布式锁概述

    除了Redis,还能使用什么作为分布式锁?

    利用Redis的setnx(SET if Not eXists)命令来实现分布式锁,由于Redis是单线程单进程,多线程同时对同一个key进行setnx的时候,只有一个能成功(集群也是一样,同一个key通过CRC函数计算,最终key会落到同一个集群的节点)。

    redis> SETNX mykey "Hello"
    (integer) 1
    redis> SETNX mykey "World"
    (integer) 0
    redis> GET mykey
    "Hello"
    redis> 
    

    2.基于Spring的RedisTemplate配置

    • SpringBoot的yaml配置
    spring:
      redis:
        host: 127.0.0.1
        port: 6379
        timeout: 20000
        pool:
          max-active: 8
          min-idle: 0
          max-idle: 8
          max-wait: -1
        password:
    
    • 代码片段
    @RestController
    public class DeductStockController {
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @PostConstruct
        public void init() {
            stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
        }
    
        @RequestMapping("/deductStock")
        public String deductStock() {
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + "");
                System.out.println("剩余库存" + realStock);
            } else {
                System.out.println("库存不足");
            }
            return "end";
        }
    
    }
    

    3.分布式锁的三种使用

    • 异常使用
    @RestController
    public class DeductStockV2Controller {
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @PostConstruct
        public void init() {
            stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
        }
    
        @RequestMapping("/deductStockV2")
        public String deductStock() {
            String lockKey = "lockKey";
            Boolean stockLock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1");
            if (!stockLock) {
                System.out.println("没有获取到锁");
                return "lock error";
            }
            int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock > 0) {
                int realStock = stock - 1;
                stringRedisTemplate.opsForValue().set("stock", realStock + "");
                System.out.println("剩余库存" + realStock);
            } else {
                System.out.println("库存不足");
            }
            stringRedisTemplate.delete(lockKey);
            return "end";
        }
    
    }
    

    哪里有问题?

    • 普通使用
    @RestController
    public class DeductStockV3Controller {
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @PostConstruct
        public void init() {
            stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
        }
    
        @RequestMapping("/deductStockV3")
        public String deductStock() {
            String lockKey = "lockKey";
            String clientId = UUID.randomUUID().toString();
            Boolean stockLock = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
    //        stringRedisTemplate.expire(lockKey, 10, TimeUnit.SECONDS);
            if (!stockLock) {
                System.out.println("没有获取到锁");
                return "lock error";
            }
            try {
                int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    int realStock = stock - 1;
                    stringRedisTemplate.opsForValue().set("stock", realStock + "");
                    System.out.println("剩余库存" + realStock);
                } else {
                    System.out.println("库存不足");
                }
            } finally {
                if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
                    stringRedisTemplate.delete(lockKey);
                }
            }
            return "end";
        }
    
    }
    
    1. 锁使用完之后应该在finally代码块中释放
    2. 释放锁的时候需要判断当前锁的对象和释放锁的对象是否一致,一致才能释放
    3. 还有什么问题?
    • 高级使用
      Redisson是Java的Redis操作库,功能强大。
    @SpringBootApplication
    public class RedisApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(RedisApplication.class, args);
        }
    
        @Bean
        public Redisson redisson() {
            Config config = new Config();
            config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);
    //        config.useClusterServers()
    //              .addNodeAddress("redis://ip:port")
    //              .addNodeAddress("redis://ip:port")
    //              .addNodeAddress("redis://ip:port");
    
            return (Redisson) Redisson.create(config);
        }
    }
    
    @RestController
    public class DeductStockV4Controller {
    
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        @Autowired
        private Redisson redisson;
    
        @PostConstruct
        public void init() {
            stringRedisTemplate.opsForValue().set("stock", String.valueOf(50));
        }
    
        @RequestMapping("/deductStockV4")
        public String deductStock() {
            String lockKey = "lockKey";
            RLock redissonLock = redisson.getLock(lockKey);
    
    //        if (!stockLock) {
    //            System.out.println("没有获取到锁");
    //            return "lock error";
    //        }
            try {
                redissonLock.lock();
                int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
                if (stock > 0) {
                    int realStock = stock - 1;
                    stringRedisTemplate.opsForValue().set("stock", realStock + "");
                    System.out.println("剩余库存" + realStock);
                } else {
                    System.out.println("库存不足");
                }
            } finally {
                redissonLock.unlock();
            }
            return "end";
        }
    
    }
    

    使用Redisson帮助我们解决了以上问题。
    lock的时候默认30秒时间,同时每隔10秒自动延长锁的时间30秒(守护线程)。

    相关文章

      网友评论

          本文标题:Redis分布式锁

          本文链接:https://www.haomeiwen.com/subject/huxacktx.html