Mastering Redis Distributed Locks: From Single‑Node to Redlock Cluster Implementation

Learn how to implement Redis distributed locks in both single‑node and cluster environments, covering lock acquisition and release commands, key pitfalls like master‑slave failover, and the advanced Redlock algorithm with step‑by‑step guidance, Lua scripts, and a PHP lock class example.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Mastering Redis Distributed Locks: From Single‑Node to Redlock Cluster Implementation

Single‑Node Lock

Acquire lock using SET with NX and PX (or EX) options, providing a unique value (e.g., a UUID). Release the lock with a Lua script that deletes the key only if the stored value matches the caller’s identifier.

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

Command syntax: SET key value PX milliseconds NX (or EX seconds).

The value must be unique for each lock holder.

Unlock must verify the value to avoid releasing another client’s lock.

The lock exists only on the master node; a master‑slave failover can cause the lock to be lost.

Redlock Algorithm for Redis Cluster

Redlock requires a majority of independent Redis master nodes to grant the lock. A common deployment uses five masters.

Record startTime (current Unix time in milliseconds).

For each master, attempt SET key uniqueValue PX ttl NX with a network timeout shorter than the lock TTL (e.g., 5‑50 ms for a 10 s lock).

If a response is not received within the timeout, move to the next master.

When locks are obtained on at least N/2+1 masters, record endTime and compute costTime = endTime - startTime. The lock is considered acquired only if costTime < ttl.

If the majority is not reached or costTime exceeds the TTL, run the unlock Lua script on all masters to release any partial locks.

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

PHP Example Using Redis and Lua Scripts

The class below demonstrates lock acquisition and release. It generates a unique identifier, retries acquisition for a configurable period, and uses the Lua scripts above to ensure atomicity.

class RedisLock
{
    /**
     * Acquire a distributed lock.
     *
     * @param string $lock_name   Lock key (without prefix)
     * @param int    $acquire_time Number of seconds to keep retrying
     * @param int    $lock_timeout TTL of the lock in seconds
     * @return string|false       Unique identifier on success, false on failure
     */
    public static function acquireLock($lock_name, $acquire_time = 3, $lock_timeout = 20)
    {
        $identifier = md5($_SERVER['REQUEST_TIME'] . mt_rand(1, 10000000));
        $key = 'AGENT_ORDER_LOCK:' . $lock_name;
        $end_time = time() + $acquire_time;

        $script = <<<luascript
local result = redis.call('setnx', KEYS[1], ARGV[1])
if result == 1 then
    redis.call('expire', KEYS[1], ARGV[2])
    return 1
elseif redis.call('ttl', KEYS[1]) == -1 then
    redis.call('expire', KEYS[1], ARGV[2])
    return 0
end
return 0
luascript;

        while (time() < $end_time) {
            $res = location_redis()->evaluate($script, [$key, $identifier, $lock_timeout], 1);
            if ($res == '1') {
                return $identifier;
            }
            usleep(100000); // 100 ms pause before next attempt
        }
        return false;
    }

    /**
     * Release a previously acquired lock.
     *
     * @param string $lock_name   Lock key (without prefix)
     * @param string $identifier Identifier returned by acquireLock()
     * @return bool                True if the lock was released
     */
    public static function releaseLock($lock_name, $identifier)
    {
        $key = 'AGENT_ORDER_LOCK:' . $lock_name;

        $script = <<<luascript
local result = redis.call('get', KEYS[1])
if result == ARGV[1] then
    if redis.call('del', KEYS[1]) == 1 then
        return 1
    end
end
return 0
luascript;

        $res = location_redis()->evaluate($script, [$key, $identifier], 1);
        return $res == 1;
    }
}

Reference Diagram

Redis cluster deployment diagram
Redis cluster deployment diagram
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.

redisPHPdistributed-lockLuaRedlock
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

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.