Mastering Distributed Locks with Redis: From Bronze to Diamond Solutions

This article examines why local locks fail in distributed systems, introduces Redis‑based distributed locking, and walks through five progressive solutions—from a simple SETNX implementation to atomic Lua scripts—highlighting each approach's drawbacks and how to mitigate them.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Mastering Distributed Locks with Redis: From Bronze to Diamond Solutions

1. Problems with Local Lock

Local locks (e.g., synchronized or lock) work for a single instance but cause data inconsistency when multiple micro‑services handle high‑concurrency requests, because each service can acquire its own lock independently.

In a scenario with 10 W requests forwarded to four services, each service may lock its thread, leading to divergent cache values and unexpected results.

2. What Is a Distributed Lock

A distributed lock ensures that only one thread across a cluster can access a critical section (e.g., a database) at a time.

Analogy: the lock is a door, and all concurrent threads are people trying to enter the room; only one person may be inside, and the door is locked until they leave.

3. Redis SETNX

Redis can serve as a shared place to store the lock. The SETNX command (SET if Not eXist) sets a key only when it does not already exist. set <key> <value> NX Example using Docker:

docker exec -it <container_id> redis-cli
set wukong 1111 NX

Result OK means the lock was acquired; subsequent attempts return nil.

4. Bronze Solution

4.1 Bronze Principle

All threads attempt SETNX. The first succeeds and holds the lock; others wait until the lock is released.

// 1. Acquire lock
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "123");
if (lock) {
  // 2. Execute business logic
  List<TypeEntity> list = getDataFromDB();
  // 3. Release lock
  redisTemplate.delete("lock");
  return list;
} else {
  // 4. Sleep and retry
  sleep(100);
  return getTypeEntityListByRedisDistributedLock();
}

4.2 Bronze Drawbacks

If the business code throws an exception or the server crashes, the lock is never released, causing a deadlock. Adding an automatic expiration mitigates this risk.

5. Silver Solution

After acquiring the lock, set an expiration time so the lock is cleared automatically.

// Set expiration after acquiring lock
redisTemplate.expire("lock", 10, TimeUnit.SECONDS);

The two‑step process (acquire then set expiration) is not atomic; if a failure occurs between the steps, the lock may never expire.

6. Gold Solution

Use Redis’s atomic SET command with both NX and an expiration ( PX for milliseconds or EX for seconds).

# Set key with value and expiration atomically
set <key> <value> PX 5000 NX
# or
set <key> <value> EX 5 NX

Checking the TTL confirms the key’s remaining lifetime.

ttl wukong

7. Platinum Solution

Assign a unique identifier (UUID) to each lock value and verify it before deletion.

// 1. Generate UUID
String uuid = UUID.randomUUID().toString();
// 2. Acquire lock with expiration
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
if (lock) {
  // Business logic
  List<TypeEntity> list = getDataFromDB();
  // 3. Verify lock ownership
  String lockValue = redisTemplate.opsForValue().get("lock");
  if (uuid.equals(lockValue)) {
    // 4. Release lock
    redisTemplate.delete("lock");
  }
  return list;
} else {
  // Wait and retry
  sleep(100);
  return getTypeEntityListByRedisDistributedLock();
}

Although the expiration prevents permanent deadlock, acquiring the lock and setting the expiration are still separate steps, so a failure between them can leave the lock without an expiration.

8. Diamond Solution

Use a Lua script to atomically check the lock value and delete it.

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

Execute the script via redisTemplate.execute:

String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class),
    Arrays.asList("lock"), uuid);

9. Summary

The article starts from the limitations of local locks, introduces Redis‑based distributed locking, and presents five progressive solutions—Bronze, Silver, Gold, Platinum, and Diamond—detailing each method’s shortcomings and how to improve them, providing practical Java code and diagrams throughout.

GitHub : https://github.com/Jackson0714/PassJava-Platform Gitee : https://gitee.com/jayh2018/PassJava-Platform Tutorial Site : www.passjava.cn
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backendredissetnx
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

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.