Analysis of Redis Distributed Lock Principle

Keywords: Redis Java github

  • Redis Distributed Lock Command
    Setnx if and only if the key does not exist. If the given key already exists, setnx does nothing. Setnx is the abbreviation of "set if not exists" (if not, set). Setnx has atomicity.
    get set first gets the old value, then sets the new value, and returns the old value of the key, which is atomic. When the key exists but is not a string type, an error is returned; when the key does not exist, it returns nil, which is null in Java.
    expire sets the validity of key
    del deletes key
  • Combination with timestamp
    The value of a distributed lock is composed of the current system time System.currentTimeMillis()+Key validity period.
  • Redis Distributed Lock Flow Chart
  • Redis Distributed Lock Optimized Edition Process

    This optimized version of the process here shows that if the lock is successfully acquired, then the business logic is executed according to the process, the lock is executed, and the lock is deleted. If the lock is not acquired, continue to determine the timestamp to see if the lock can be reset and acquired. First get the current value, and compare the current system time and the value obtained. If the current time is greater than the value, the lock has expired, then perform the getset operation to reset the value of the key. If the old value returned does not exist, say If the key has been deleted, or the old value exists, and the value is the same as that of the previous get, it means that the lock is actually acquired at this time, and then the corresponding business logic is executed.
// For example, this code to close the order
@Scheduled(cron = "0 */1 * * * ?")// Execute every minute
public void closeOrderTask() {
    log.info("Close Order Timing Task Start");
    long lockTimeOut = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
    Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeOut));
    if (setnxResult != null && setnxResult.intValue() == 1) {
        closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
    } else {
        // If the lock is not acquired, continue to determine the timestamp to see if the lock can be reset and acquired.
        String lockValueA = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        if (lockValueA != null && System.currentTimeMillis() > Long.parseLong(lockValueA)) {
            String lockValueB = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, String.valueOf(System.currentTimeMillis() + lockTimeOut));
            //Use the current timestamp getSet again
            //Returns the given old key value, > the old value to determine whether a lock can be acquired
            //When the key is not worthwhile, that is, when the key does not exist, return to nil - > to get the lock
            //Here we set a new value to get the old value
            if (lockValueB == null || (lockValueB != null && StringUtils.equals(lockValueA, lockValueB))) {
                //Real Lock Acquisition
                closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            } else {
                log.info("No distributed lock was acquired:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
            }
        } else {
            log.info("No distributed lock was acquired:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        }
    }
    log.info("Close Order Timing Task End");
}
 private void closeOrder(String lockName) {
        RedisShardedPoolUtil.expire(lockName, 5);// Set validity 50 seconds to prevent deadlock. On-line production environment should be set to 5 seconds.
        log.info("Obtain{},ThreadName:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());
        int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.out", "2"));
        iOrderService.closeOrder(hour);
        RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
        log.info("release{},ThreadName:{}", Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK, Thread.currentThread().getName());

        log.info("********************************************");
    }

Click on GitHub to view the code

Posted by errtu on Fri, 10 May 2019 23:30:02 -0700