How to Implement Distributed Lock with Redis
To implement a distributed lock in Redis, use the
SET command with the NX option to set a key only if it does not exist, combined with PX to set an expiration time. This ensures only one client holds the lock at a time, preventing conflicts in distributed systems.Syntax
The basic Redis command to acquire a distributed lock is:
SET key value NX PX milliseconds- key: The lock name.
- value: A unique identifier for the client (e.g., UUID).
- NX: Only set the key if it does not already exist (lock acquisition).
- PX milliseconds: Set the lock expiration time in milliseconds to avoid deadlocks.
To release the lock safely, use a Lua script that deletes the key only if the value matches your unique identifier.
redis
SET resource_lock unique_client_id NX PX 30000Output
"OK" or null if lock not acquired
Example
This example shows how to acquire and release a distributed lock in Redis using Python and the redis-py library.
python
import redis import uuid client = redis.Redis() lock_key = "resource_lock" lock_value = str(uuid.uuid4()) lock_expire = 30000 # 30 seconds in milliseconds # Try to acquire the lock acquired = client.set(lock_key, lock_value, nx=True, px=lock_expire) if acquired: print("Lock acquired") # Critical section here # Lua script to release lock safely release_script = """ if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end """ result = client.eval(release_script, 1, lock_key, lock_value) if result == 1: print("Lock released") else: print("Lock not released: value mismatch") else: print("Failed to acquire lock")
Output
Lock acquired
Lock released
Common Pitfalls
- Not setting expiration: Without expiration, a lock can stay forever if the client crashes, causing deadlocks.
- Releasing lock without checking value: Deleting the lock key blindly can remove another client's lock.
- Using non-unique values: Use unique IDs per client to avoid releasing others' locks.
- Long critical sections: If your task takes longer than expiration, the lock may expire early, causing race conditions.
redis
Wrong way (no value check): DEL resource_lock Right way (Lua script): if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end
Quick Reference
| Command | Purpose | Notes |
|---|---|---|
| SET key value NX PX milliseconds | Acquire lock if not exists | Returns OK if lock acquired, null otherwise |
| DEL key | Release lock (unsafe) | May delete others' locks, avoid this |
| Lua script with get and del | Safe lock release | Deletes key only if value matches |
| GET key | Check lock owner | Returns lock value or null |
Key Takeaways
Use SET with NX and PX options to acquire a distributed lock safely in Redis.
Always set an expiration time to prevent deadlocks if the client crashes.
Release locks using a Lua script that checks the lock owner to avoid deleting others' locks.
Use unique values per client to identify and manage locks correctly.
Avoid long-running tasks that exceed the lock expiration time to prevent race conditions.