Operations 21 min read

Common Pitfalls and Best Practices of Redis Distributed Locks

This article examines the typical mistakes when using Redis distributed locks—such as non‑atomic operations, forgetting to release locks, releasing others' locks, lock starvation, re‑entrancy issues, lock granularity, timeout handling, and master‑slave replication problems—and provides practical solutions and code examples for each scenario.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Common Pitfalls and Best Practices of Redis Distributed Locks

Redis distributed locks are popular for their simplicity and efficiency, but improper usage can lead to serious issues. This article outlines the most common pitfalls and offers concrete solutions with Java code examples.

1. Non‑Atomic Operations

Using SETNX followed by EXPIRE is not atomic; if setting the timeout fails, the lock may never expire, causing memory leaks.

if (jedis.setnx(lockKey, val) == 1) {
    jedis.expire(lockKey, timeout);
}

The atomic alternative is the SET command with NX and PX options:

String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
    return true;
}
return false;

2. Forgetting to Release the Lock

Even with the atomic SET , the lock must be released explicitly, preferably in a finally block to guarantee execution.

try {
    String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
    if ("OK".equals(result)) {
        // business logic
        return true;
    }
    return false;
} finally {
    unlock(lockKey);
}

3. Releasing Someone Else’s Lock

In multi‑threaded scenarios, a thread may delete a lock that belongs to another thread. The solution is to store a unique requestId as the lock value and verify it before deletion:

if (jedis.get(lockKey).equals(requestId)) {
    jedis.del(lockKey);
    return true;
}
return false;

A Lua script can make this check atomic:

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

4. Massive Failed Requests

When many clients compete for a single lock (e.g., flash‑sale scenarios), most requests fail. Using a spin lock with timeout and retry logic can improve success rates:

long start = System.currentTimeMillis();
while (true) {
    String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
    if ("OK".equals(result)) {
        // critical section
        return true;
    }
    if (System.currentTimeMillis() - start >= timeout) {
        return false;
    }
    Thread.sleep(50);
}

5. Lock Re‑entrancy

Recursive methods that try to acquire the same lock will dead‑lock. Redisson provides a re‑entrant lock that tracks a counter per requestId :

RLock lock = redisson.getLock(lockKey);
lock.lock(5, TimeUnit.SECONDS);
// recursive calls can safely lock again

6. Lock Granularity

Coarse‑grained locks cause high contention. Using read‑write locks or segmenting locks (e.g., sharding a stock counter into 100 parts) reduces contention and improves throughput.

// Read‑write lock example
RReadWriteLock rwLock = redisson.getReadWriteLock("readWriteLock");
RLock rLock = rwLock.readLock();
RLock wLock = rwLock.writeLock();

7. Lock Timeout

If a lock expires before the business logic finishes, subsequent code runs without protection. Automatic lock renewal (watch‑dog) solves this, either via Redisson’s built‑in feature or a custom TimerTask that periodically extends the TTL.

Timer timer = new Timer();
 timer.schedule(new TimerTask() {
     @Override
     public void run() {
         // extend lock TTL
     }
 }, 10000, TimeUnit.MILLISECONDS);

8. Master‑Slave Replication Issues

When the master crashes before replicating a newly acquired lock, a newly elected slave may lose the lock, breaking mutual exclusion. Redisson’s RedissonRedLock implements the Redlock algorithm across multiple independent Redis instances to mitigate this risk.

// Redlock acquisition (simplified)
RedissonRedLock redLock = new RedissonRedLock(node1Lock, node2Lock, node3Lock, node4Lock, node5Lock);
redLock.lock();

Overall, while Redis locks are powerful, developers must handle atomicity, proper release, re‑entrancy, timeout renewal, and multi‑node consistency to avoid subtle bugs.

ConcurrencyRedisDistributed LockRedissonPitfalls
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.