Redis分布式锁

2020-07-14  本文已影响0人  zouhao1985

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配置

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. 还有什么问题?
@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秒(守护线程)。

上一篇下一篇

猜你喜欢

热点阅读