关于 Redis分布式锁 详细过程

关于 Redis分布式锁 详细过程

简而言之Redis分布式锁的秒杀原理

首先必须清楚一个事实: 秒杀期间 1000个请求 可能只有10个获取锁并且秒杀成功的请求 其余990个将抛出异常(因为拿不到分布式锁)这就是秒杀分布式锁导致的结果(无数个请求尝试获取一个锁)
必须清楚有关redis操作的方法:
StringRedisTemplate.opsForValue().setIfAbsent(key, value) -> 原子性设置key和value(如果不存在此key:true 如果存在:false 不修改)
StringRedisTemplate.opsForValue().get(key)->获取value
StringRedisTemplate.opsForValue().getAndSet(key, value)->原子性先获取value之后设置自己的value值

开始代码思路
key:商品ID
value:当前时间+超时时间

上锁代码思路开始:设置key和value

1.开始:无数个请求拿分布式锁
2.判断(预期结果:存在此key/不存在此key):setIfAbsent(key,value) 原子性设置 并检查是否存在此key(返回true/false)不存在则设置自己的key和value,存在就直接返回false(只有一个线程可以设置到key和value)
3.(不存在此key)返回 true(意味获得分布式锁成功
4.(存在此key)获取:get(key) 多个线程同时拿到当前的currentvalue
5.判断(预期结果:锁过期&&currentvalue不为null/currentvalue为null/锁没过期):检查是否超时了并且当前的currentvalue不为null,获取当前时间与currentvalue比较
6.获取并设置(锁过期了):多个线程 开始 原子性地 再次获取之前的currentvalue2然后 设置自己的value(同时只有一个线程可以设置)(一直到)
6.1 (可能多个线程) 判断(锁过期了)(预期结果:currentvalue2与currentvalue一致&&currentvalue不为null):这里只能让第一个修改了value的线程并判断true而成功获得锁,后来的线程都是拿到前一个线程所改变的value。
6.2 (currentvalue2与currentvalue一致&&currentvalue不为null)获得分布式锁成功
7.(锁没过期/currentvalue为null)此线程直接抛出异常

代码:

/**
     * 加锁
     * @param key
     * @param value 当前时间+超时时间
     * @return
     */
    private StringRedisTemplate redisTemplate;
    public boolean lock(String key, String value) {
    //多个线程抢锁(设置key和value)
        if(redisTemplate.opsForValue().setIfAbsent(key, value)) {
        //获得分布式锁成功
            return true;
        }
        //currentValue=A   这两个线程的value都是B  其中一个线程拿到锁
        String currentValue = redisTemplate.opsForValue().get(key);
        //如果锁过期
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁的时间
            log.info("锁过期了!!!");
            String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }
        }

        return false;
    }

解锁代码思路开始:清除key和value

开始拿value值
判断是否跟当前的value值相等
如果相等则清除key和value

代码:


    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key, String value) {
        try {
            String currentValue = redisTemplate.opsForValue().get(key);
            //拿到redis的value
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
            //清除redis的key和value
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e) {
            log.error("【redis分布式锁】解锁异常, {}", e);
        }
    }

代码猜想

可能超时条件成立的时候存在死锁:(其实并不会存在死锁) 代码会连续收到百万请求,如果加锁的时候判断到前
一个锁超时此时可能会存在多个线程进来依次执行 redisTemplate.opsForValue().getAndSet(key, value);
这个语句。 此时会筛选掉除第一个之后的线程进入获取锁。但是value可能是判断锁过期后,最后一个线程设置的value值,在线程解锁的时候会出现 当前currentvalue != value导致无法解锁,这时候需要等到一种情况才能脱离暂时性的死锁,那也就是 判断这次的锁过期的时候,进来修改value值的线程只有一个的时候,才能顺利地获得锁并且解锁,但是前面无法解锁的也可能已经把逻辑执行完成了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值