redis分布式锁实现的正确姿势及问题

redis分布式锁的四大特性:

1.互斥性,同一时间只有一台服务器能获取到锁

2.不会发生死锁,当某台服务器获取到锁后因为故障导致不能释放锁,其他服务器也能正常获取锁

3.容错性,大多数redis节点存活下,分布式锁任然有效,即不会因为一个节点挂而导致锁失效

4.解铃还需系铃人,加锁和解锁必须由同一个服务器来执行(防止因业务执行时间过长导致缓存时间已过) 

说到redis分布式锁,我们都会说用setnx命令,再设置一个超时时间expireTime,但真的是这样的吗?

其实不然,如果使用两个命令来执行redis操作,那如果执行完setnx后发生异常,导致过期时间没有设置上,则会导致redis发生死锁,不满足特性2.(两个操作不是原子性)

那怎么做呢...

在较高版本的redis中,提供了set(key,value,set_if_not_exist,set_with_expire_time,expireTime),该操作保证了设置值和设置过期时间的原子性。

在上述set命令中,value值建议使用uuid作为唯一值,这样在解锁的时候可以用来唯一标示服务器

解锁时需要比较存的值,值一致才删除这个key

那怎么才能保证上述操作的原子性呢,答案就是lua脚本

public boolean release(String identify) {
    if(identify == null){
            return false;
        }

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = new Object();
        try {
            result = jedis.eval(script, Collections.singletonList(lockKey),
                Collections.singletonList(identify));
        if (RELEASE_SUCCESS.equals(result)) {
            log.info("release lock success, requestToken:{}", identify);
            return true;
        }}catch (Exception e){
            log.error("release lock due to error",e);
        }finally {
            if(jedis != null){
                jedis.close();
            }
        }

        log.info("release lock failed, requestToken:{}, result:{}", identify, result);
        return false;
    }

使用lua脚本解决了多个操作的原子性问题,那是不是分布式锁就没问题了呢,理想情况下是没问题了!

但是:

如果redis是多节点的,redis是单进程单线程的写入和读取,主节点负责写请求,从节点负责读请求,它保证数据的最终一致性。

如果分布式锁在主节点上,主节点恰好挂了,那么会从从节点上选一个来作为主节点,那此时是不是分布式锁就失效了呢,并不是,redis引入了RedLock(红锁)来保证redis锁的容错性。

红锁的实现:

过程:

1.对多个redis节点进行RLock,并将其构造成RedLock

2.如果循环加锁失败,允许重试次数为集群的最大个数,最终判断加锁失败的个数,比如3个允许失败1个,5个允许失败2个,保证大多数能加锁成功。

3.加锁过程设置一个过期时间,比如4ms,如果加锁时间(总时间)超过这个值则返回加锁失败。

 

除此之外影响因素还有GC的STW(stop the word),时钟跳跃等,有兴趣的可以取看看

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周润发的弟弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值