redis distributed lock problems and solutions

Distributed Lock

In a distributed environment, in order to ensure normal access to business data, to prevent the problem repeat request, it uses a distributed lock to block subsequent requests. Let's write a business code in question:

  public void doSomething(String userId){
        User user=getUser(userId);
        if(user==null){
            user.setUserName("xxxxx");
            user.setUserId(userId);
            insert(user);
            return;
        }
        update(user);
    }

The above code is very simple, user query db has no corresponding data, if any, the update operation, if no insert.

We know that the above code is thread-unsafe in a multithreaded environment, there will be problems. In order to ensure the correctness of the data, in stand-alone environment, we can use the synchronizedmethod to ensure thread safety, specific changes:

  public synchronized void doSomething(String userId){
        User user=getUser(userId);
        if(user==null){
            user.setUserName("xxxxx");
            user.setUserId(userId);
            insert(user);
            return;
        }
        update(user);
    }

In the single-machine environments, to solve thread safety issues, and that it in a distributed environment? This time need to use 分布式锁.

Distributed Lock need the help of other components to achieve, commonly used redisand zookeeper. Here we use to achieve the redis, to illustrate the next issue, distributed lock specific method is as follows

    public  void doSomething(String userId){
        String lock=RedisUtils.get("xxxx"+userId);
        if(StringUtils.isNotEmpty(lock)){//说明当前userId已经被锁定
            return;
        }
        RedisUtils.set("xxxx"+userId,userId,1000);//锁定10s
        User user=getUser(userId);
        if(user==null){
            insert(user);
            RedisUtils.delete("xxxx"+userId);
            return;
        }
        update(user);
        RedisUtils.delete("xxxx"+userId);
        
    }

The code above solves the problem in a distributed environment concurrent. But also we need to consider a question, if the insert operation and the update operation exception, and will not be distributed lock release, subsequent requests will be blocked.

So we re-optimization, an increase of catching exceptions.

 public  void doSomething(String userId){
        try {
                String lock=RedisUtils.get("xxxx"+userId);
                if(StringUtils.isNotEmpty(lock)){//说明当前userId已经被锁定
                    return;
                }
                RedisUtils.set("xxxx"+userId,userId,1000);//锁定1s
                User user=getUser(userId);
                if(user==null){
                    insert(user);
                    return;
                }
                update(user);
        }
        catch(Exception ex){

        }
        finally{
            RedisUtils.delete("xxxx"+userId);
        }
    }

 Even now the program is abnormal, the lock will be released automatically. But redis get and set there will concurrency issues, we'll continue to optimize the use of redis setnxmethod

    public  void doSomething(String userId){
        try {
                boolean lock=RedisUtils.setnx("xxxx"+userId,userId,1000);//锁定1s
                if(!lock){//说明当前userId已经被锁定
                    return;
                }
                User user=getUser(userId);
                if(user==null){
                    insert(user);
                    return;
                }
                update(user);
        }
        catch(Exception ex){

        }
        finally{
            RedisUtils.delete("xxxx"+userId);
        }
    }

 

 

The above code does not seem to be any problem, but there is a big risk. Under our analysis, assuming that the first request came, the implementation of successful, a program is running, but the insert and update operations blocked 1s, a second request is over, lock the cache has expired, the implementation of the second lock is successful, the first time a request to complete the lock is released, the second lock request was the first request to release the third time the request will cause the thread insecurity.

How to optimize it go? The main problem is the emergence of the first request in question mistakenly deleted the lock, so we removed the lock time to determine whether to remove.

Ideas: we locked when, value using the current time stamp to determine whether the period expired but if you do not delete when deleted, specific code as follows:

public  void doSomething(String userId){
        try {
                boolean lock=RedisUtils.setnx("xxxx"+userId,LocalDateTime.now(),1000);//锁定10s
                if(!lock){//说明当前userId已经被锁定
                    return;
                }
                User user=getUser(userId);
                if(user==null){
                    insert(user);
                    return;
                }
                update(user);
        }
        catch(Exception ex){

        }
        finally{
            LOCKTIME the LocalDateTime = RedisUtils.get ( "XXXX" + the userId);
             IF (lockTIme.compare (LocalDateTime.now ()) <0 ) {
                 // description has expired can be deleted Key 
                RedisUtils.delete ( "XXXX" + the userId); 
            } 
        } 
    }

 

 

 So even if there is obstruction, the second time stamp to cover the first lock, so that even if the first is completed, it will not release the lock.

Guess you like

Origin www.cnblogs.com/OceanHeaven/p/11220285.html