Redis handles distributed locks

  The last chapter talked about locking in a single application to solve the cache breakdown problem, but in distributed, there will be many services for each service. If you use local locks, it only locks its own services, not all of them. The database is only queried once in the service, so in this case, we can consider using distributed locks
  

Fundamental

Insert picture description here
  All services go to a public place to hold the lock. When a service gets the lock, it can execute related logic, while other services are in a waiting state. This public place can be MySQL or Redis. Of course, our service development uses Redis for caching. It is definitely more convenient to lock in Redis, and Redis itself also provides the corresponding lock command. For details, please check the official document ( portal )
Insert picture description here
EX seconds-settings specified to Period time, in seconds.
PX milliseconds-Set the specified expiration time (in milliseconds).
NX-Only set the key when the key does not exist, which is equivalent to a lock in Redis, and the thread waits. We use this based on this
XX-Only set the key when the key exists.
KEEPTTL-Keep the time to live associated with the key.
GET-returns the old value stored in the key; if the key does not exist, it returns nil.
  Let’s test this command first. You can see that when I set data to Redis and use NX at the same time, only one succeeds. This is the basic principle of distributed locks
Insert picture description here
  expressed in Java code.

public void redisLock() throws InterruptedException {
    
    
    // 通过NX方式存值,占分布式锁
    Boolean flag = template.opsForValue().setIfAbsent("lock", "lock is not exist");
    if (flag) {
    
    //占锁成功
        //执行业务
        System.out.println(template.opsForValue().get("lock"));
        //业务执行成功后,需要删除锁给其他人用
        template.delete("lock");
    } else {
    
    //占锁失败,进行重试
        TimeUnit.SECONDS.sleep(50);
        redisLock();
    }
}

  This lock is equivalent to a placeholder. When the service gets the lock, it can carry out the following logic code, while the service that does not get the lock can only wait. This lock does not participate in the actual business,
  but this lock will be derived One problem, when our business code is abnormal, and the process ends without deleting the lock, the lock will always be occupied, which causes a deadlock. This problem can be solved by setting the expiration time, which is set when the lock is added. The expiration time of the lock, but to ensure the atomicity of these two operations

public void redisLock() throws InterruptedException {
    
    
    // 通过NX方式存值,占分布式锁,设置过期时间防止死锁
    Boolean flag = template.opsForValue().setIfAbsent("lock", "lock is not exist",30,TimeUnit.SECONDS);
    if (flag) {
    
    //占锁成功
        //执行业务
        System.out.println(template.opsForValue().get("lock"));
        //业务执行成功后,需要删除锁给其他人用
        template.delete("lock");
    } else {
    
    //占锁失败,进行重试
        TimeUnit.SECONDS.sleep(50);
        redisLock();
    }
}

  The same problem occurs when deleting locks. For example, the execution time of business logic is longer, and the expiration time of locks is shorter. When the business has not been executed, the lock has expired, and other businesses can already occupy the lock. After a business logic is executed, the lock is deleted, and the lock of other services is deleted. Solution: Specify the unique value of uuid for your service

public void redisLock() throws InterruptedException {
    
    
    // 通过NX方式存值,占分布式锁,设置过期时间防止死锁
    String uuid = UUID.randomUUID().toString();
    Boolean flag = template.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
    if (flag) {
    
    //占锁成功
        //执行业务
        String lock = template.opsForValue().get("lock");
        System.out.println(lock);
        //业务执行成功后,需要删除锁给其他人用
        if (lock.equals(uuid)){
    
    //判断当前锁的值,防止误删其他服务的锁
            template.delete("lock");
        }
    } else {
    
    //占锁失败,进行重试
        TimeUnit.SECONDS.sleep(50);
        redisLock();
    }
}

  There are other problems when deleting the lock. When the value of the lock is acquired, due to the delay of network transmission, the lock is invalid before the deletion is performed after the comparison is successful, so the deleted lock at this time is still other The service lock is caused by the time-consuming network transmission. This requires the use of lua scripts for processing. Redis officially recommends us to do this, and our code cannot directly handle it, so it finally evolved into the following the result of

public void redisLock() throws InterruptedException {
    
    
    // 通过NX方式存值,占分布式锁,设置过期时间防止死锁
    String uuid = UUID.randomUUID().toString();
    Boolean flag = template.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
    if (flag) {
    
    //占锁成功
        try {
    
    
            //执行业务
            String lock = template.opsForValue().get("lock");
            System.out.println(lock);
        } finally {
    
    
            //业务执行成功后,需要删除锁给其他人用
            //lua脚本解锁
            String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
            //删除锁
            template.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
        }
    } else {
    
    //占锁失败,进行重试
        TimeUnit.SECONDS.sleep(50);
        redisLock();
    }
}

  There are more powerful and professional frameworks to deal with distributed locks. The next chapter will use Redisson to solve these problems.

Guess you like

Origin blog.csdn.net/weixin_45481406/article/details/113272570