Understanding Redisson Distributed Locks: Implementation, Reentrancy, Fairness, and Watchdog Mechanism
This article explains Redisson's distributed lock features, comparing it with Jedis and Lettuce, detailing lock acquisition, reentrancy, Lua scripts, watchdog renewal, and the implementation of fair locks using Redis lists and sorted sets, with code examples and architectural insights.
Redisson is a Java in‑memory data grid built on Redis that provides high‑level distributed objects and services, including a powerful distributed lock implementation. Compared with Jedis and Lettuce, Redisson offers a richer API and built‑in lock semantics such as reentrancy and automatic lease renewal.
The article first shows a simple lock using RedisTemplate where setIfAbsent (SETNX) is used to acquire a lock and a UUID is checked before releasing it. This approach suffers from non‑atomic get‑and‑del operations, which can cause lock loss under contention.
To solve the atomicity problem, Lua scripts are introduced. The lock script ( lock.lua ) creates a hash entry for the lock name, stores the thread identifier, and sets an expiration. The unlock script ( unlock.lua ) decrements a reentrancy counter and deletes the key when the counter reaches zero, publishing an unlock message via Redis Pub/Sub.
-- lock.lua
local key = KEYS[1]
local threadId = ARGV[2]
local leaseTime = ARGV[1]
if redis.call('exists', key) == 0 then
redis.call('hset', key, threadId, 1)
redis.call('pexpire', key, leaseTime)
return nil
end
if redis.call('hexists', key, threadId) == 1 then
redis.call('hincrby', key, threadId, 1)
redis.call('pexpire', key, leaseTime)
return nil
end
return redis.call('pttl', key) -- unlock.lua
local key = KEYS[1]
local threadId = ARGV[2]
if redis.call('hexists', key, threadId) == 0 then return nil end
local counter = redis.call('hincrby', key, threadId, -1)
if counter > 0 then
redis.call('pexpire', key, ARGV[1])
return 0
else
redis.call('del', key)
redis.call('publish', KEYS[2], ARGV[3])
return 1
endRedisson’s RLock (the core RedissonLock class) wraps these scripts and adds a watchdog mechanism. When a lock is acquired without an explicit lease time, Redisson schedules a periodic task (every leaseTime/3 ) that re‑executes a renewal Lua script to extend the expiration, preventing accidental lock release during long business operations.
The watchdog renewal script looks like:
if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then
redis.call('pexpire', KEYS[1], ARGV[1])
return 1
end
return 0Unlocking also uses Pub/Sub: when the lock is released, a message is published on a channel; waiting threads subscribe to this channel, run a callback, and release a semaphore that allows the next thread to retry acquisition.
Beyond the default non‑fair lock, Redisson provides a fair lock implementation ( RedissonFairLock ) that guarantees FIFO ordering using a Redis list (queue) and a sorted set (timeout map). The Lua script for the fair lock performs six steps: cleaning expired queue entries, attempting first‑time acquisition, handling reentrancy, returning the current TTL, calculating the tail node’s TTL, and finally enqueuing the thread with a computed score based on current time and wait timeout.
-- simplified fair lock Lua steps
while true do
local head = redis.call('lindex', KEYS[2], 0)
if not head then break end
if redis.call('zscore', KEYS[3], head) < ARGV[4] then
redis.call('zrem', KEYS[3], head)
redis.call('lpop', KEYS[2])
else break end
end
if redis.call('exists', KEYS[1]) == 0 and (redis.call('exists', KEYS[2]) == 0 or redis.call('lindex', KEYS[2], 0) == ARGV[2]) then
redis.call('lpop', KEYS[2])
redis.call('zrem', KEYS[3], ARGV[2])
redis.call('hset', KEYS[1], ARGV[2], 1)
redis.call('pexpire', KEYS[1], ARGV[1])
return nil
end
if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then
redis.call('hincrby', KEYS[1], ARGV[2], 1)
redis.call('pexpire', KEYS[1], ARGV[1])
return nil
end
-- return TTL for waiting threads ...Overall, the article demonstrates how Redisson leverages Redis Lua scripting, Pub/Sub, and Netty‑based asynchronous futures to provide robust, reentrant, and optionally fair distributed locks, while also explaining the underlying concepts such as lock renewal (watchdog) and the trade‑offs between fairness and performance.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.