Mastering Distributed Locks with Redis: From Basics to Advanced Solutions

This article examines why local locks fail in distributed micro‑service environments, introduces Redis‑based distributed locking, walks through five incremental lock designs—from a simple SETNX implementation to a Lua‑script atomic solution—highlighting each scheme's trade‑offs, code examples, and practical pitfalls.

Architect
Architect
Architect
Mastering Distributed Locks with Redis: From Basics to Advanced Solutions

Local in‑process locking (e.g., synchronized or lock) cannot guarantee consistency when a request flow is split across multiple micro‑services; each service may acquire its own lock, leading to cache‑breakdown and divergent results.

1. The Problem with Local Locks

When 100,000 front‑end requests are distributed to four services (25,000 each) and a cache miss forces every service to lock its own thread, the lock only protects the local JVM. Service A may update key = 100 while Service B concurrently updates key = 99, leaving the final value nondeterministic.

2. What Is a Distributed Lock?

A distributed lock must work across a cluster so that only one thread can access a critical section at a time. The article uses the everyday analogy of a door lock: only one person may be inside the room; others must wait until the door is unlocked.

3. Redis SETNX

Redis provides the SETNX command (set if not exists). When the key does not exist, it is created; otherwise nothing happens. set <key> <value> NX Example in a Docker container:

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

The command returns OK on success and nil on failure.

4. Bronze Scheme (Simple SETNX)

4.1 Principle

All threads attempt SETNX. The first succeeds and holds the lock.

Other threads fail and must wait.

When the holder finishes, it deletes the lock.

Waiting threads retry the acquisition.

Java code using Spring’s RedisTemplate:

// 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. Sleep briefly then retry
    sleep(100);
    return getTypeEntityListByRedisDistributedLock();
}

Issue: if the business code throws an exception or the process crashes, the lock is never released, causing a deadlock.

5. Silver Scheme (Lock with Expiration)

5.1 Principle

After acquiring the lock, an expiration time is set so that the lock automatically disappears after a fixed period.

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

Full code combines acquisition and expiration:

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

Problem: acquiring the lock and setting the expiration are two separate steps; if a crash occurs between them, the lock may remain without an expiration, re‑introducing deadlock.

6. Gold Scheme (Atomic SET with Expiration)

6.1 Principle

Redis’ SET command can atomically set a value with an expiration using the NX and PX / EX options.

# Milliseconds expiration
set <key> <value> PX <ms> NX
# Seconds expiration
set <key> <value> EX <seconds> NX

Java example:

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

This guarantees that the lock is created only if it does not exist and that it will expire after the specified time.

7. Platinum Scheme (Unique Identifier)

7.1 Principle

To avoid a client deleting another client’s lock, a unique UUID is stored as the lock value.

// 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();
    // 4. Verify ownership before deletion
    String current = redisTemplate.opsForValue().get("lock");
    if (uuid.equals(current)) {
        redisTemplate.delete("lock");
    }
    return list;
} else {
    // Retry after short sleep
    sleep(100);
    return getTypeEntityListByRedisDistributedLock();
}

Drawback: the check‑then‑delete sequence is still non‑atomic; a lock may expire and be reacquired by another client between the read and delete steps.

8. Diamond Scheme (Lua Script for Atomic Check‑And‑Delete)

8.1 Principle

A Lua script runs inside Redis, atomically comparing the stored value with the expected UUID and deleting the key only if they match.

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

Execution from Java:

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

This eliminates the race condition described in the platinum scheme.

9. Summary of Schemes

Bronze : Simple SETNX. Deadlock if lock not released.

Silver : Adds expiration, but acquisition and expiration are separate → possible deadlock.

Gold : Atomic SET with expiration solves the previous race.

Platinum : Stores a unique UUID to avoid deleting others' locks, yet check‑then‑delete is non‑atomic.

Diamond : Uses Lua script for atomic check‑and‑delete, providing the safest approach before moving to a dedicated library such as Redisson.

These incremental improvements illustrate how to reason about concurrency hazards, evaluate trade‑offs, and progressively harden a distributed locking mechanism.

Reference material:

http://redis.cn/commands/set.html

https://www.bilibili.com/video/BV1np4y1C7Yf

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.

JavaMicroservicesconcurrencyRedisspringDistributed LockLua
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.