Redis分布式锁的演化

1 篇文章 0 订阅
本文探讨了使用Redis实现分布式锁时遇到的问题及改进措施。从最初的简单分布式锁开始,逐步解决异常处理、服务器宕机、锁过期、锁续命等问题,确保锁的正确释放和原子性,以防止死锁和并发问题。
摘要由CSDN通过智能技术生成

使用redis来实现一把分布式锁

普通分布式锁

一把普通的redis分布式锁,原理是setnx只有在第一次设置的时候才生效,等于第一个setnx成功的线程得到了这把锁,其他线程设置失败就直接返回了,只有当这个线程执行完毕,把这个锁给删除,别的线程才能得到这个锁。

    //伪代码
    private Jedis jedis;
    public String doLock(){
        boolean result = jedis.setnx("lockKey","Lock");//setnx 只能当redis里面没有这个key的时候才能设置成功
        if(!result){
            return "error";
        }
        //主体业务逻辑
        jedis.delete("lockKey");

        return null;
    }

思考:如果主体业务中抛了异常怎么办?在执行主体业务逻辑的时候,向外抛出了一个异常,那么就没有执行释放锁的过程,这样程序就死锁了

改进

由于我们一定要释放这个锁,也就是让释放锁的操作总要执行,所以应该把释放锁的操作放进try - finally里面,这样,抛了异常也能释放这个锁了。

    private Jedis jedis;
    public String doLock(){
        boolean result = jedis.setnx("lockKey","Lock");//setnx 只能当redis里面没有这个key的时候才能设置成功
        if(!result){
            return "error";
        }
        try {
            //主体业务逻辑
        }finally {
            jedis.delete("lockKey");
        }

        return null;
    }

思考:如果在执行业务逻辑的时候发生了服务器宕机怎么办?直接宕机的话还是没有执行到finally语句,这样这个线程的锁依然没有释放,照样死锁了。

改进

可以设置一个过期时间,如果这个线程宕机,导致死锁,但是10s之后这个key过期了,也可以把这个锁释放掉

	    private Jedis jedis;
        boolean result = jedis.setnx("lockKey","Lock");//setnx 只能当redis里面没有这个key的时候才能设置成功
        jedis.expire("lockKey",10);//设置过期时间为10s
        if(!result){
            return "error";
        }
        try {
            //主体业务逻辑
        }finally {
            jedis.delete("lockKey");
        }

        return null;
    }

思考:如果程序执行到设置过期时间的时候宕机了怎么办?这样的话并没有成功的给这把锁设置好过期时间。

改进

保持设置锁和锁的过期时间的一致性就可以了

    private Jedis jedis;
    public String doLock(){
        boolean result = jedis.set("lockKey","Lock","NX","EX",10);//setnx 只能当redis里面没有这个key的时候才能设置成功
        if(!result){
            return "error";
        }
        try {
            //主体业务逻辑
        }finally {
            jedis.delete("lockKey");
        }

        return null;
    }

思考:如果主体业务逻辑执行的时间非常长,而给这个锁设置的时间比较短,在这个线程执行的时候还没有执行完,这个锁已经过期了,这个锁就会被别的线程得到,当第一个线程执行完释放锁的时候,释放的可能就是第二个线程的锁,如果是高并发的环境下,就直接乱套了,谁也不知道这个线程释放的是谁的锁。

改进

可以定义一个唯一的id,就可以让线程只能释放自己的锁,这样就不会被别的线程释放了

    private Jedis jedis;
    private String keyId = UUID.randomUUID().toString();//线程的唯一标识
    public String doLock(){
        boolean result = jedis.set("lockKey",keyId,"NX","EX",10);//setnx 只能当redis里面没有这个key的时候才能设置成功
        if(!result){
            return "error";
        }
        try {
            //主体业务逻辑
        }finally {
            if(keyId.equals(jddis.get(lockKey))){//只能释放自己的锁
                jedis.delete("lockKey");
            }
        }

        return null;
    }

思考:这样虽然当前线程的锁不会被别的线程的锁释放,但是如果这个线程的锁还是过期了,那么别的线程也可以得到锁,可以进行主体业务逻辑的执行,这样就不能保证执行的原子性了。

改进

可以在业务逻辑中加一个timer计时器,每隔一段时间对锁进行重新设置过期时间,也就是对锁续命,这样这个锁就算很完美啦

//没玩过计时器,代码不会写
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值