Skip to content
Navigation

Type at least two characters. Search covers page titles, headings, tags and database names.

↑ ↓ to navigateEnter to openEsc to close0 pages
Redis and Valkeyadvanced

Redis Distributed Locks

Implementing a lock correctly with SET NX PX and a fencing-aware release, and being honest about what it cannot guarantee.

3 min readAdvancedUpdated Edit this page

Redis is often used for mutual exclusion. It can do this usefully, and it cannot do it safely in the strong sense. Both halves matter.

The minimum correct implementation

Three properties are required: atomic acquire-with-expiry, a unique owner token, and a release that verifies ownership.

# Acquire: atomic set-if-absent with a TTL and a unique token.
SET lock:orders:4711 8f14e45fceea167a5a36dedd4bea2543 NX PX 30000
-- Release: delete only if this client still owns it. Must be atomic.
if redis.call('GET', KEYS[1]) == ARGV[1] then
  return redis.call('DEL', KEYS[1])
else
  return 0
end
import secrets
 
token = secrets.token_hex(16)
acquired = r.set(f"lock:orders:{order_id}", token, nx=True, px=30_000)
if acquired:
    try:
        do_work()
    finally:
        # Never DEL unconditionally: the lock may already belong to someone else.
        r.eval(RELEASE_SCRIPT, 1, f"lock:orders:{order_id}", token)

A non-atomic release — GET, compare in the client, then DEL — can delete another client's lock if the TTL expires between the two commands. The Lua script closes that gap.

What this still does not guarantee

The mitigation for the first problem is fencing: the lock returns a monotonically increasing token, and the protected resource rejects operations carrying a token older than the newest one it has seen. That requires cooperation from the resource, which most storage systems do not provide — which is precisely why this is hard.

The Redlock algorithm, which acquires the lock on a majority of independent Redis instances, reduces the failover problem. Its safety under clock drift and process pauses has been publicly disputed, and it does not solve the expiry problem at all.

Practical guidance

Practical rules if you do use one:

  • Set the TTL longer than the worst realistic execution time, and extend it from a watchdog if the work is long-running.
  • Make the protected operation idempotent anyway.
  • Always release with the ownership check.
  • Alert on lock acquisition failures — a lock that is never released is a silent stall.