The introduction

When we are learning any programming language, we are more or less exposed to locks provided by the language level or related libraries. Consider, for example, the locking function provided by Sync. Mutex in Golang. Here, Golang’s locks come in handy when two Goroutines are competing for critical resources (usually a write operation), where one goroutine acquires the lock and the other goroutine waits until the lock is released to operate on the critical resources. However, language-level locks can often only prevent concurrency problems within a single process or thread, and are not effective when multiple processes or clients are snapping up an item on an e-commerce site. This is where distributed locks are needed. There are many distributed locks, such as Zookeeper, Redis and so on. Here, we mainly introduce distributed locks based on Redis.

A distributed lock

Distributed locks are generally used to control access to critical resources by distributed systems or multiple systems.

Characteristics of distributed locks

  1. Mutex: The basic feature of a lock that can only be held by one process (thread) at a time
  2. Timeout release: The lock must be released after a certain period of time to prevent unexpected problems caused by lock failure
  3. High performance vs. high availability: Locking and releasing locks should be low overhead, and unexpected lock failures should be minimized

Distributed lock based on Redis single machine

Use the SETNX command

The SETNX command of Redis returns 1 if kv is set successfully, and 0 otherwise. Using this feature, we can implement a simple distributed lock. For example, if a commodity is locked in a seckill activity, the commodity number is taken as key and any value is set as value, the SETNX command is executed successfully, then the lock is successfully locked. After the critical section task is executed, the key is deleted to release the lock.

SETNX key value
do something
DEL key 
Copy the code

However, there is a problem with this. If an exception occurs during the execution of a critical section task, the delete operation cannot be performed properly, resulting in the failure to release the lock, and the resource will remain locked

Make use of the extended command SET

In the above case, it is easy to think of adding an expiration time to key

SETNX key value
EXPIRE key 10
do something
DEL key
Copy the code

However, there is no atomic operation between SETNX and EXPIRE commands, so it is possible that SETNX succeeds, but EXPIRE fails, and the situation becomes the same again. The extended parameter to Redis’s SET command provides a way to combine the SETNX and EXPIRE commands into atomic operations

SET key NX EX 10
do something
DEL key
Copy the code

While the SETNX and EXPIRE commands are not atomic operations, there are other issues. If thread A obtains the lock, but executes the critical section task beyond the expiration time of the lock, then the lock is released, thread B obtains the lock to perform the task, and thread A completes the task, then deletes the lock, this time will delete thread B’s lock by mistake. Therefore, we need a CAD(Compare And Delete) mechanism to release locks. When we talk about SET, value is a random number (thread ID can be used, etc.). Therefore, when deleting a key, compare it with the value of the key first. If the value is equal, release the lock (delete key). However, checking value equality and deleting key are not atomic operations, and Redis currently does not have relevant extension parameters to support this atomic operation, so you can consider using Lua scripts to execute multiple instruction atoms. Many companies’ scaffolding departments actually have a layer of CAD – like commands that the business side can use directly.

Further reading

Although the above command solves the atomic operation problem, it still does not solve the problem that the task locked in the critical region is not completed but is released due to timeout. Consider something like this: the thread that acquired the lock also opens an additional thread, which checks for the lock every ExpireTime/3. If the lock exists, reset the lock expiration time to ExpireTime to prevent the lock from being released early. Of course, the actual may not be so simple, interested friends can refer to: github.com/redisson/re… . In addition, there are distributed locks based on Redis multi-machine implementation, interested friends can also go to know about it.