Evolution of Redis Distributed Locks and Redisson Implementation Analysis

This article explains how distributed lock requirements arise when scaling from monolithic to multi‑server architectures, reviews the evolution of Redis‑based locking mechanisms from simple SETNX to Lua scripts and Redisson's advanced features, and provides detailed source‑code analysis of Redisson's lock, unlock, and lock acquisition implementations.

360 Quality & Efficiency
360 Quality & Efficiency
360 Quality & Efficiency
Evolution of Redis Distributed Locks and Redisson Implementation Analysis

When an application moves from a single‑machine monolithic setup to a distributed environment with multiple servers, local JVM locks (synchronized, ReentrantLock) can no longer guarantee mutual exclusion across processes, so a third‑party global lock is required.

Among the common third‑party solutions—database locks, Redis distributed locks, and ZooKeeper locks—Redis is the most widely used. This article walks through the evolution of Redis distributed lock implementations.

01 Based on SETNX

SETNX is an atomic Redis command that sets a key only if it does not already exist. The typical pattern is:

if (setnx(key, 1) == 1) {
    expire(key, 30);
    try {
        // TODO business logic
    } finally {
        del(key);
    }
}

Steps: 1) Attempt to set the lock key; 2) Set an expiration to avoid deadlocks; 3) Delete the key after the business logic. The problem is that acquiring the lock and setting the expiration are two separate commands, so if the process crashes after acquiring the lock, the lock may never expire.

02 Based on SET

Since Redis 2.6.12, the SET command can set a value with an expiration atomically, solving the above issue:

if (set(key, value, 30)) {
    try {
        // TODO business logic
    } finally {
        del(key);
    }
}

The drawback is that if the business logic runs longer than the 30‑second TTL, the lock expires and another client may acquire it; the original client may then delete the new lock, causing a lock‑steal problem.

03 Based on SET with UUID

Introducing a UUID to identify the lock owner ensures that only the thread that set the lock can release it:

String uuid = UUID.randomUUID().toString();
if (set(key, uuid, 30)) {
    try {
        // TODO business logic
    } finally {
        if (get(key) == uuid) {
            del(key);
        }
    }
}

However, the check‑then‑delete sequence is still non‑atomic, so if the lock expires between the check and the delete, another client may acquire the lock and be mistakenly unlocked.

04 Based on SET with Lua script

Using Redis' EVAL command to run a Lua script makes the check‑and‑delete operation atomic:

if (set(key, uuid, 30)) {
    try {
        // TODO business logic
    } finally {
        EVAL(
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) else return 0 end",
            1, key, uuid
        );
    }
}

The remaining issue is in a Redis cluster: if the master node fails before the lock command replicates to replicas, a new master may not have the lock, allowing another client to acquire it and causing duplicate lock acquisition.

05 Based on Redisson

Redisson provides a high‑level lock implementation that includes a watchdog thread automatically extending the lock’s TTL, preventing the lock‑expire‑while‑executing problem. Example usage:

RLock lock = redisson.getLock("foobar"); // 1. obtain lock object
lock.lock(); // 2. acquire lock
try {
    // TODO business logic
} finally {
    lock.unlock(); // 3. release lock
}

Redisson’s source code shows how the lock ID (a global UUID), lease time, and watchdog are managed. The getLock() method creates a RedissonLock instance, which stores the lock name combined with the client ID. The unlock() method calls unlockAsync(), which uses a Lua script via EVAL to delete the lock only if the current thread is the owner, ensuring atomicity and preventing accidental unlocks.

Further analysis of lock() reveals that it repeatedly attempts to acquire the lock using tryAcquire(), subscribes to a Redis channel to receive lock‑release notifications, and uses semaphores to block until the lock becomes available. Once the lock is obtained, the subscription is cancelled.

Overall, the article provides a comprehensive timeline of Redis distributed lock techniques, highlights their pitfalls, and demonstrates how Redisson abstracts these complexities into a robust, production‑ready locking library.

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.

concurrencyredisLockredisson
360 Quality & Efficiency
Written by

360 Quality & Efficiency

360 Quality & Efficiency focuses on seamlessly integrating quality and efficiency in R&D, sharing 360’s internal best practices with industry peers to foster collaboration among Chinese enterprises and drive greater efficiency value.

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.