1. 分布式锁的实现
加锁操作
- Lua 脚本:
-
- 这个脚本用于尝试获取分布式锁。
- 它会检查锁是否已经被其他客户端持有,并尝试设置锁。
- 参数说明:
-
KEYS[1]
: 表示锁的 key,即锁的名称。ARGV[1]
: 表示当前请求的线程 ID 或客户端标识。ARGV[2]
: 表示锁的过期时间(毫秒)。
- 脚本详解:
local lockKey = KEYS[1]
local threadId = ARGV[1]
local leaseTime = ARGV[2]
local currentTime = redis.call('TIME')
local expireTime = tonumber(currentTime[1]) * 1000 + tonumber(currentTime[2]) / 1000000 + leaseTime
-- 尝试获取锁
local acquired = redis.call('SET', lockKey, threadId .. ':' .. expireTime, 'NX', 'PX', leaseTime * 1000)
if acquired then
return true
end
-- 锁已经被持有
local currentValue = redis.call('GET', lockKey)
if currentValue then
local currentThreadId, currentExpireTime = unpack(string.match(currentValue, "(.*):(.*)"))
if currentThreadId == threadId and currentExpireTime < expireTime then
-- 如果当前持有者是自己并且未过期,更新过期时间
redis.call('PEXPIRE', lockKey, leaseTime * 1000)
return true
end
end
return false
- 步骤解释:
- 获取当前时间:
- 使用
redis.call('TIME')
获取当前 Unix 时间戳(秒)和微秒。计算出过期时间(毫秒)。 - 尝试获取锁:
- 使用
redis.call('SET', lockKey, threadId .. ':' .. expireTime, 'NX', 'PX', leaseTime * 1000)
尝试获取锁。 - 如果成功(
acquired
为1
),则返回true
- 检查锁状态:
- 如果锁已被持有,获取锁的当前持有者信息。
- 如果锁被其他客户端持有,则返回
false
。 - 如果当前持有者是自己并且未过期,则更新锁的过期时间,并返回
true
。
解锁操作
- Lua 脚本:
- 这个脚本用于释放分布式锁。
- 参数说明:
KEYS[1]
: 表示锁的 key,即锁的名称。ARGV[1]
: 表示当前请求的线程 ID 或客户端标识。- 脚本详解:
local lockKey = KEYS[1]
local threadId = ARGV[1]
-- 获取锁的持有者信息
local currentValue = redis.call('GET', lockKey)
if currentValue then
local currentThreadId, _ = unpack(string.match(currentValue, "(.*):(.*)"))
if currentThreadId == threadId then
-- 如果当前持有者是自己,释放锁
redis.call('DEL', lockKey)
return true
end
end
return false
- 步骤解释:
- 检查锁状态:
- 获取锁的当前持有者信息。如果锁被其他客户端持有,则返回
false
。 - 如果当前持有者是自己,则删除锁,并返回
true
。
2. 设置过期时间
实际上,设置过期时间已经在加锁操作的 Lua 脚本中实现了。当尝试获取锁时,如果成功,则会同时设置锁的过期时间。因此,不需要单独的 Lua 脚本来设置过期时间。
Lua 脚本的执行
- 执行方式:
- 使用
EVAL
命令执行 Lua 脚本。 EVAL
命令接受一个 Lua 脚本和一个或多个键值对作为参数。- 使用
EVALSHA
命令可以更高效地执行预先计算好的 SHA1 哈希值的脚本。 - 示例:
- 执行 Lua 脚本:
String script = "your lua script here";
List<String> keys = Arrays.asList("lockKey");
List<String> args = Arrays.asList("threadId", "leaseTime");
String result = jedis.eval(script, keys, args);
总结
通过 Lua 脚本,Redisson 能够确保分布式锁的一系列操作(获取锁、设置过期时间、释放锁)的原子性,这对于实现分布式锁等关键功能至关重要。Lua 脚本不仅可以确保操作的原子性,还能提高性能,因为它可以避免客户端和服务器之间的多次往返通信。