Redis distributed lock principle and redison implementation

Time:2021-11-22

Redis distributed lock principle

Redis distributed locking principle, you can directly see the official documents:
https://redis.io/commands/set…

The command SET resource-name anystring NX EX max-lock-time is a simple way to implement a locking system with Redis.

SET resource-name anystring NX EX max-lock-timeThe command can implement distributed locks based on redis.

  • NX Only set the key if it does not already exist
  • EX seconds Set the specified expire time, in seconds
  • NXThe setting is successful only when the key does not exist
  • EX secondsFailure time (seconds)

A client can acquire the lock if the above command returns OK (or retry after some time if the command returns Nil), and remove the lock just using DEL.

  • When the command returnsOKWhen, the client obtains the lock
  • When the command returnsNilWhen, the client does not obtain the lock. It needs to retry the command after a period of time to try to obtain the lock
  • useDELThe delete command can be used to release the lock

The lock will be auto-released after the expire time is reached.

When the expiration time is reached, the lock is automatically released.

It is possible to make this system more robust modifying the unlock schema as follows:

  • Instead of setting a fixed string, set a non-guessable large random string, called token.
  • Instead of releasing the lock with DEL, send a script that only removes the key if the value matches.

This avoids that a client will try to release the lock after the expire time deleting the key created by another client that acquired the lock later.

More robust ways to release locks:

  • The set value is a randomly generated and unpredictable value called token
  • Instead of using del to directly delete keys to release locks, a script is used to delete keys only when value matches token

This can prevent a client from trying to release the lock after the expiration time. Using del directly may delete the lock added by other clients.

The following is an example of a lock release script:

if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del",KEYS[1])
else
    return 0
end

The script should be called with EVAL ...script... 1 resource-name token-value

implementEVAL ...script... 1 resource-name token-valueCommand to release the lock.

The above is the content in the official documents. You can find a problem here:

  • In the official scheme, the distributed lock has an expiration time. When the expiration time is reached, the lock will be automatically released. If the task to be locked has not been completed and the lock is obtained by other clients, serious problems may occur;
  • If the lock is not added with the expiration time, if the client that obtains the lock suddenly crashes and does not have time to release the lock, the lock will never be released.

To solve this problem, let’s see how redisson solves it.

Redisson distributed lock

Official documents:
https://github.com/redisson/r…

You can obtain a key withmyLockofRLockObject:

Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");

Acquire and release locks:

lock.lock(); //  Acquire lock
try {
  ...
} finally {
  lock.unlock(); //  Release the lock in finally
}

RLockThe following methods for obtaining locks are provided:

  • void lock()
  • void lock(long leaseTime, TimeUnit unit)
  • void lockInterruptibly()
  • void lockInterruptibly(long leaseTime, TimeUnit unit)
  • boolean tryLock()
  • boolean tryLock(long time, TimeUnit unit)
  • boolean tryLock(long waitTime, long leaseTime, TimeUnit unit)

RLockRealizedjava.util.concurrent.locks.LockInterface, soRLockIs in line with JavaLockInterface specification. Of the above methods, these four methods are derived from JavaLockInterface:

  • void lock()Obtain the lock. If the lock is unavailable, the current thread waits until it obtains the lock
  • void lockInterruptibly()andlock()The method is similar, but the difference islockInterruptibly()Method can be interrupted by interrupt while waiting
  • boolean tryLock()Obtain the lock without waiting, and immediately return a value of boolean type to indicate whether the acquisition is successful
  • boolean tryLock(long time, TimeUnit unit)Obtain the lock. If the lock is unavailable, wait for a period of time. The maximum waiting time is determined bylong timeandTimeUnit unitThe two parameters specify that false will be returned if the lock is not obtained after a period of time, and true will be returned if the lock is obtained successfully

In addition to the above four methods, there are three methods that are not derived from JavaLockInterface, butRLockMethods in. The biggest difference between these three methods and the above four methods is that there is one morelong leaseTimeParameters.leaseTimeIt refers to the expiration time of the key in redis. If the lock obtained through these three methods reachesleaseTimeIf the lock is not released, the lock will automatically expire.

Back to the above question: if the expiration time is set, the lock will be automatically released when the task is not completed and the expiration time is reached; If the failure time is not set, the lock will never be released if it suddenly crashes. How did redisson solve this problem?

If Redisson instance which acquired lock crashes then such lock could hang forever in acquired state. To avoid this Redisson maintains lock watchdog, it prolongs lock expiration while lock holder Redisson instance is alive. By default lock watchdog timeout is 30 seconds and can be changed through Config.lockWatchdogTimeout setting.

To prevent the redisson instance from crashing and causing the lock to never be released, for unspecifiedleaseTimeRedisson maintains a watchdog for locks. The watchdog extends the failure time of the lock at regular intervals. The default expiration time of the lock is 30 seconds, which can be passedConfig.lockWatchdogTimeoutModification. The execution frequency of the task extending the expiration time is also determined by this configuration item, which is 1 / 3 of the lock expiration time, that is, it is executed every 10 seconds by default.

If the redisson instance crashes, the watchdog will follow the crash. When the expiration time is reached, the key will be automatically cleared by redis, and the lock will be released. The lock will not be permanently occupied.

Scan code to pay attention to my official account.

Redis distributed lock principle and redison implementation