Pankaj Chauhan Pankaj Pankaj home
System Design

How Redis Distributed Locks Work

A practical explanation of Redis distributed locking, Redlock, fencing tokens, and the failure modes that show up in production.

Pankaj Chauhan Updated 2 min read
Editorial illustration of a lock over a Redis-style key grid

A distributed lock is a way for multiple processes to agree that only one of them may enter a critical section. Redis is a common place to put that lock because it is fast, widely deployed, and supports atomic compare-and-set operations.

The difficulty is not acquiring a key. The difficulty is remaining correct when packets are delayed, a process pauses, or a node thinks it still owns a lock that Redis has already expired.

The simplest Redis lock

The core primitive is SET with NX and an expiry:

SET lock:orders NX PX 10000

NX means “set only if the key does not exist.” PX attaches a time-to-live so the lock cannot live forever if the owner crashes.

Release should be ownership-safe. A naive DEL is wrong if another client has already taken the lock after expiry. The usual pattern is a Lua script that deletes the key only when the value matches a unique token:

if redis.call("get", KEYS[1]) == ARGV[1] then
  return redis.call("del", KEYS[1])
else
  return 0
end

That token is typically a UUID generated by the client before acquire.

Why expiry exists

Without a TTL, a crashed owner leaves the system stuck. With a TTL, you introduce a new hazard: the owner may still be working after Redis expires the key.

That is the central trade-off:

  • Too short a TTL and healthy work gets interrupted.
  • Too long a TTL and a crash blocks the cluster.

A heartbeat that extends the TTL (PEXPIRE) helps, but it does not remove the pause problem. A garbage-collected runtime can freeze for longer than your TTL. A network partition can delay the unlock.

Single-instance Redis is not enough for some designs

If you only talk to one Redis primary, the lock is only as available as that primary. During failover, a replica that had not received the SET can become primary. Two clients can then believe they hold the lock.

This is why people discuss Redlock: acquire the lock on a majority of independent Redis nodes, using roughly the same token and a short clock window.

Redlock is controversial. It can reduce some failover races, but it still depends on timing assumptions. If you need a lock to protect correctness rather than efficiency, Redis may be the wrong tool.

Use a Redis lock to avoid duplicate work. Do not use it as the only fence around money movement, unique inventory, or any operation that cannot be made idempotent.

Fencing tokens

A more robust pattern is a monotonically increasing token. ZooKeeper and etcd can give you this naturally. With Redis you can approximate it by storing a version and requiring every write to carry that version.

The storage layer then rejects stale writers:

UPDATE inventory
SET quantity = quantity - 1, lock_token = 42
WHERE sku = 'abc' AND lock_token = 41;

If the original lock holder wakes up late, its write does not land. The lock no longer has to be perfectly exclusive at every instant; the data plane enforces a happens-before.

Practical guidance

  1. Prefer idempotent jobs. A lock then becomes an optimization.
  2. Always set a TTL and a unique owner token.
  3. Unlock with a compare-and-delete script, never a bare DEL.
  4. If correctness matters, add a fencing token in the database or queue.
  5. Measure pause times in your runtime before you pick a TTL.

Redis locks are simple to demo and subtle to operate. Treat them as a lease, not as a mutex from a textbook on shared-memory threads.