Redis分布式锁浅析

redis有一个命令: set key value nx;
该命令的用处是,只有当redis中不含key时,才能set成功。
基于以上原理,可以设计分布式锁。
分布式锁可用于防止redis缓存击穿,也可解决幂等性问题。

分布式锁设计思路:

  1. 为防止在解锁前服务器突然宕机,导致死锁,redis分布式锁会设置一个过期时间。
// (1)相当于redis中的set lock value nx
redisTemplate.opsForValue().setIfAbsent("lock", "value");
// (2)设置过期时间
redisTemplate.expire("lock", 30, TimeUnit.SECONDS);
  1. 上述写法仍存在问题,即在(1)和(2)两个操作之间,redis宕机了,仍会造成死锁问题。因此加锁和设置过期时间得设计成原子性操作。
// 设置锁和过期时间操作是原子的
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "val", 300, TimeUnit.SECONDS);
  1. 我们的业务完成之后,应立马释放锁以防止阻塞其它进程。
// 设置锁和过期时间操作是原子的
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "val", 300, TimeUnit.SECONDS);

if (lock) {
  System.out.println("获取分布式锁成功...");
  //加锁成功... 执行业务
  try {
      // 执行业务代码
      // ......
  } finally {
      //删除锁
      redisTemplate.delete("lock");
  }
}
  1. 上述流程又有一个问题,假如请求A业务超时,超过了设置的30s,这个时候锁过期了便会自动删除。这个时候请求B进来了,并加了分布式锁。然后请求A手动delete的操作就会把请求B加的锁给删了,导致误删。为了解决这个问题,可将分布式锁的value设置一个指定值,删的时候看看是不是自己的锁。
// 设置锁和过期时间操作是原子的,并指定uuid,以便删锁的时候判断是不是自己的。
String uuid = UUID.randomUUID().toString();
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);

if (lock) {
    System.out.println("获取分布式锁成功...");
    //加锁成功... 执行业务
    try {
        // 执行业务代码
        // ......
    } finally {
        String lockValue = redisTemplate.opsForValue().get("lock");
        if(uuid.equals(lockValue)){
            //删除我自己的锁
            redisTemplate.delete("lock");//删除锁
        }
    }
  1. 上述操作就完美了吗?其实还是有问题。如果在get("lock")delete("lock")之间锁过期了,仍然会导致误删。因此get("lock")delete("lock")也要一气呵成。可用lua脚本实现。
// 设置锁和过期时间操作是原子的,并指定uuid,以便删锁的时候判断是不是自己的。
String uuid = UUID.randomUUID().toString();
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);

if (lock) {
    System.out.println("获取分布式锁成功...");
    //加锁成功... 执行业务
    try {
        // 执行业务代码
        // ......
    } finally {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        //删除锁
        Long lock1 = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class)
                , Arrays.asList("lock"), uuid);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值