Unlock the redis lock in the correct position

  • 2020-05-14 05:20:54
  • OfStack

Unlock the redis lock in the correct position

redis is a good friend of php. In the business writing process of php, the concept of lock is sometimes used, and only one person can operate a certain behavior at the same time. This is where we're going to use locks. There are several ways of locking, php can't use locks in memory, zookeeper can't be used to lock, and it is expensive to use the database for locking, so we usually choose redis as the locking mechanism at this time.

setnx

The simplest data structure locked in redis is string. In the early days, the lock operation 1 used setnx, this command is when :lock does not exist set1 val, perhaps you will remember to use expire to increase the expiration of the lock, to unlock the operation is to use del command, the pseudo-code is as follows:


if (Redis::setnx("my:lock", 1)) {
  Redis::expire("my:lock", 10);
  // ... do something

  Redis::del("my:lock")
}

There's a problem here, but the problem is that if you have setnx and expire and you have crash and so on, maybe this lock won't be released. The next step might be to store timestamp in lock. Determine the length of timestamp.

set

It is now officially recommended to use set directly for locking. Instead of setnx, we can use the set command, which looks like this


if (Redis::set("my:lock", 1, "nx", "ex", 10)) {
  ... do something

  Redis::del("my:lock")
}

The code above sets my:lock to 1, and if and only if the lock does not exist, the expiration time is set to 10 when the setting is complete.

The mechanism for acquiring locks is correct, but the mechanism for removing locks is incorrect to use del directly. Because may cause the mistake to delete someone else's lock the situation.

For example, I locked this lock with 10s, but it took me longer to process it than 10s, and at 10s, the lock automatically expired, was taken away, and was locked again. At this point, I call Redis::del to delete the lock that someone else has created.

There is also an official recommendation on the unlocking command, which suggests using the lua script to do get first and del later

The program becomes:


$token = rand(1, 100000);

function lock() {
  return Redis::set("my:lock", $token, "nx", "ex", 10);
}

function unlock() {
  $script = `
if redis.call("get",KEYS[1]) == ARGV[1]
then
  return redis.call("del",KEYS[1])
else
  return 0
end  
  `
  return Redis::eval($script, "my:lock", $token)
}

if (lock()) {
  // do something

  unlock();
}

The token here is a random number, when lock toward redis my: is this token lock put unlock, in the first next get1 lock token, and if I want to delete the token is 1 to illustrate this lock is my set before, otherwise, this lock has expired, set is others, I should not do anything about it.

So: stop using setnx and use set for lock implementation.


Related articles: