Distributed Lock Implementations with Redis: From Local Locks to Bronze, Silver, Gold, Platinum, and Diamond Solutions

This article explains why local in‑memory locks fail in distributed environments, introduces the concept of distributed locks, and walks through five progressive Redis‑based lock implementations—bronze, silver, gold, platinum, and diamond—detailing their principles, code examples, advantages, and shortcomings.

Wukong Talks Architecture
Wukong Talks Architecture
Wukong Talks Architecture
Distributed Lock Implementations with Redis: From Local Locks to Bronze, Silver, Gold, Platinum, and Diamond Solutions

The previous article covered using local memory caching to improve performance, but local locking ( synchronized or lock) does not work in distributed scenarios, so this piece explores how to introduce a distributed lock.

1. Problems with local locks – When multiple micro‑services handle high‑concurrency requests, each service may acquire a local lock while accessing the database, leading to data inconsistency because locks are not shared across the cluster.

2. What is a distributed lock? – A lock that works across a distributed cluster so that only one thread can access a critical section at a time. The article uses the analogy of a single door that only one person can enter.

3. Redis SETNX – The SETNX command (set if not exists) can be used to implement a simple lock. Example command: set <key> <value> NX Running the command inside a Docker container:

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

If the key does not exist, Redis returns OK; otherwise it returns nil.

4. Bronze scheme – Use SETNX to acquire the lock and delete it after the business logic finishes. Sample Java code:

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

The bronze scheme suffers from deadlock if the business code crashes before releasing the lock.

5. Silver scheme – Adds an automatic expiration time to the lock to avoid deadlock:

// acquire lock
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "123");
if (lock) {
    // set expiration (10 seconds)
    redisTemplate.expire("lock", 10, TimeUnit.SECONDS);
    // business logic
    List<TypeEntity> list = getDataFromDB();
    redisTemplate.delete("lock");
    return list;
}

However, because acquiring the lock and setting the expiration are two separate steps, a failure between them can still leave the lock without an expiration.

6. Gold scheme – Performs lock acquisition and expiration atomically using the extended SET command:

set <key> <value> NX PX <milliseconds>

Java example:

setIfAbsent("lock", "123", 10, TimeUnit.SECONDS);

Its drawback is that all clients use the same lock value, so a client that finishes later may unintentionally delete another client’s lock.

7. Platinum scheme – Generates a unique UUID for each lock instance and stores it as the lock value, then only deletes the lock if the stored value matches the UUID:

// 1. generate UUID
String uuid = UUID.randomUUID().toString();
// 2. try to acquire lock with expiration
Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
if (lock) {
    // business logic
    List<TypeEntity> list = getDataFromDB();
    // 4. verify ownership before deleting
    String current = redisTemplate.opsForValue().get("lock");
    if (uuid.equals(current)) {
        redisTemplate.delete("lock");
    }
    return list;
} else {
    // wait and retry
    sleep(100);
    return getTypeEntityListByRedisDistributedLock();
}

Even with unique IDs, the check‑then‑delete steps are not atomic, which can still cause a race condition.

8. Diamond scheme – Solves the atomicity issue by using a Lua script that checks the lock value and deletes it in a single Redis operation:

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

Java execution example:

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);

The article concludes that the diamond scheme provides a safe distributed lock, and hints that the next article will cover Redisson as a more robust solution.

Summary – By iteratively improving from a simple SETNX lock to a Lua‑script‑based atomic operation, the article demonstrates how to handle lock acquisition, expiration, uniqueness, and atomic release in a Redis‑backed distributed system.

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.

Backendjavaconcurrencyredisspring
Wukong Talks Architecture
Written by

Wukong Talks Architecture

Explaining distributed systems and architecture through stories. Author of the "JVM Performance Tuning in Practice" column, open-source author of "Spring Cloud in Practice PassJava", and independently developed a PMP practice quiz mini-program.

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.