Methods to implement distributed locks
Use Redis's SET command
Redis'sSET
The command supports setting key-value pairs and can be passedNX
andEX
Parameters to implement atomic operations, thereby realizing distributed locking.
- NX: The key is set only when the key does not exist.
- EX: Set the expiration time (seconds) of the key.
Sample code
Here is a sample code that implements distributed locks using Go and Redis:
package main import ( "context" "fmt" "log" "time" "/go-redis/redis/v8" ) var ctx = () func main() { // Initialize the Redis client rdb := (&{ Addr: "localhost:6379", // Redis Address Password: "", // password DB: 0, // Database number }) // The key name and timeout of the lock key := "my_lock" timeout := * 10 // Try to acquire the lock lockAcquired := acquireLock(ctx, rdb, key, timeout) if lockAcquired { defer releaseLock(ctx, rdb, key) // Perform the locking operation here ("Lock acquired, performing critical section operations...") ( * 5) // Simulation time-consuming operation ("Critical section operations completed.") } else { ("Failed to acquire lock.") } } // acquireLock try to acquire the lockfunc acquireLock(ctx , client *, key string, timeout ) bool { // Set key-value pairs, set only when the key does not exist, and set the expiration time result, err := (ctx, key, "locked", timeout).Result() if err != nil { ("Failed to acquire lock: %v", err) } return result } // releaseLock releases the lockfunc releaseLock(ctx , client *, key string) { // Delete key err := (ctx, key).Err() if err != nil { ("Failed to release lock: %v", err) } }
Things to note
- Timeout: Set a reasonable timeout to prevent deadlock. If the process holding the lock crashes, the lock will not be occupied forever.
-
Idepotency: Ensure that the operation of releasing the lock is idempotent, that is, multiple calls
releaseLock
No problem. - Competition conditions: In high concurrency scenarios, competition conditions may occur. Atomic operations can be ensured through Lua scripts.
-
Security: Make sure that only processes holding the lock can release the lock. Can be passed in
SET
Set unique values in the command to achieve this.
Ensure atomicity using Lua scripts
To ensure that the operation of releasing the lock is atomic, it can be implemented using a Lua script. Here is an improved example:
package main import ( "context" "fmt" "log" "time" "/go-redis/redis/v8" ) var ctx = () func main() { // Initialize the Redis client rdb := (&{ Addr: "localhost:6379", // Redis Address Password: "", // password DB: 0, // Database number }) // The key name and timeout of the lock key := "my_lock" value := "unique_value" timeout := * 10 // Try to acquire the lock lockAcquired := acquireLock(ctx, rdb, key, value, timeout) if lockAcquired { defer releaseLock(ctx, rdb, key, value) // Perform the locking operation here ("Lock acquired, performing critical section operations...") ( * 5) // Simulation time-consuming operation ("Critical section operations completed.") } else { ("Failed to acquire lock.") } } // acquireLock try to acquire the lockfunc acquireLock(ctx , client *, key, value string, timeout ) bool { // Set key-value pairs, set only when the key does not exist, and set the expiration time result, err := (ctx, key, value, timeout).Result() if err != nil { ("Failed to acquire lock: %v", err) } return result } // releaseLock releases the lockfunc releaseLock(ctx , client *, key, value string) { // Use Lua script to ensure that the operation to release the lock is atomic script := (` if ("get", KEYS[1]) == ARGV[1] then return ("del", KEYS[1]) else return 0 end `) err := (ctx, client, []string{key}, value).Err() if err != nil { ("Failed to release lock: %v", err) } }
useSET
Commands and Lua scripts ensure the atomicity and security of operations.
====================
In the implementation of distributed locks,key
is a very important parameter, which is used to uniquely identify a lock. The following detailed explanationkey
existacquireLock
Functions of the method:
The role of key
-
Unique ID lock:
-
key
is a string that uniquely identifies a specific lock. Different locks should have differentkey
, this ensures that different resources can be locked independently. - For example, if you have two resources
resource1
andresource2
, you can set different for them separatelykey
,for example"lock:resource1"
and"lock:resource2"
。
-
-
The status of the storage lock:
- When you try to acquire the lock,
key
Used as a key in Redis. If this key already exists, it means that other clients have already held the lock. - If the key does not exist, Redis will set this key and set its value to the value you provide (for example
"locked"
or a unique identifier).
- When you try to acquire the lock,
-
Set expiration time:
- While setting the key, you can set an expiration time for the key (using
EX
Parameters). This prevents the lock from being permanently occupied due to client crashes or other reasons. - The expiration time ensures that even if there is a problem with the client holding the lock, the lock will eventually be automatically released.
- While setting the key, you can set an expiration time for the key (using
Key usage in the sample code
In the previous example code,key
Used foracquireLock
Method:
func acquireLock(ctx , client *, key, value string, timeout ) bool { // Set key-value pairs, set only when the key does not exist, and set the expiration time result, err := (ctx, key, value, timeout).Result() if err != nil { ("Failed to acquire lock: %v", err) } return result }
- key: The key used to uniquely identify the lock.
- value: Set the value of the key, which can be a fixed string (such as "locked") or a unique identifier (such as the unique ID of the client).
- timeout: Set the expiration time of the key, in seconds.
Specific examples
Suppose you have two resources resource1 and resource2, you can set different keys for them respectively:
key1 := "lock:resource1" key2 := "lock:resource2" // Try to get the lock of resource1lockAcquired1 := acquireLock(ctx, rdb, key1, "unique_value1", * 10) if lockAcquired1 { defer releaseLock(ctx, rdb, key1, "unique_value1") // Perform the locking operation here ("Lock acquired for resource1, performing critical section operations...") ( * 5) // Simulation time-consuming operation ("Critical section operations completed for resource1.") } else { ("Failed to acquire lock for resource1.") } // Try to get the lock of resource2lockAcquired2 := acquireLock(ctx, rdb, key2, "unique_value2", * 10) if lockAcquired2 { defer releaseLock(ctx, rdb, key2, "unique_value2") // Perform the locking operation here ("Lock acquired for resource2, performing critical section operations...") ( * 5) // Simulation time-consuming operation ("Critical section operations completed for resource2.") } else { ("Failed to acquire lock for resource2.") }
KEY
key
It plays a role in uniquely identifying the lock in the implementation of distributed locks. By setting different resourceskey
, it can ensure that different resources can be locked independently. at the same time,key
It is also used to store the state of the lock and can set the expiration time to prevent deadlocks.
This is the article about the common methods of Go using Redis to implement distributed locks. For more information about Go Redis distributed locks, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!