1 加锁Lua脚本
| 参数 |
示例值 |
含义 |
| KEY个数 |
1 |
KEY个数 |
| KEYS[1] |
my_first_lock_name |
锁名 |
| ARGV[1] |
60000 |
持有锁的有效时间:毫秒 |
| ARGV[2] |
58c62432-bb74-4d14-8a00-9908cc8b828f:1 |
唯一标识:获取锁时set的唯一值,实现上为redisson客户端ID(UUID)+线程ID |
-- 若锁不存在:则新增锁,并设置锁重入计数为1、设置锁过期时间
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
-- 若锁存在,且唯一标识也匹配:则表明当前加锁请求为锁重入请求,故锁重入计数+1,并再次设置锁过期时间
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 redis.call('pttl', KEYS[1]);

2 解锁Lua脚本
| 参数 |
示例值 |
含义 |
| KEY个数 |
2 |
KEY个数 |
| KEYS[1] |
my_first_lock_name |
锁名 |
| KEYS[2] |
redisson_lock__channel:{my_first_lock_name} |
解锁消息PubSub频道 |
| ARGV[1] |
0 |
redisson定义0表示解锁消息 |
| ARGV[2] |
30000 |
设置锁的过期时间;默认值30秒 |
| ARGV[3] |
58c62432-bb74-4d14-8a00-9908cc8b828f:1 |
唯一标识;同加锁流程 |
-- 若锁不存在:则直接广播解锁消息,并返回1
if (redis.call('exists', KEYS[1]) == 0) then
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
-- 若锁存在,但唯一标识不匹配:则表明锁被其他线程占用,当前线程不允许解锁其他线程持有的锁
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
return nil;
end;
-- 若锁存在,且唯一标识匹配:则先将锁重入计数减1
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) then
-- 锁重入计数减1后还大于0:表明当前线程持有的锁还有重入,不能进行锁删除操作,但可以友好地帮忙设置下过期时期
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else
-- 锁重入计数已为0:间接表明锁已释放了。直接删除掉锁,并广播解锁消息,去唤醒那些争抢过锁但还处于阻塞中的线程
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
return nil;

3 看门狗机制
- 自动续期:当使用 Redisson 的
tryLock 或 lock 方法获取锁时,如果没有指定 leaseTime 参数,Redisson 会启动看门狗线程。
- 默认有效期:锁的默认有效期是 30 秒(可通过
lockWatchdogTimeout 参数配置)。
- 续期机制:看门狗线程会在锁有效期过去 1/3 时间(即 10 秒)后检查客户端是否还持有锁,如果是,则自动将锁有效期重置为 30 秒。
- 持续检查:这个过程会不断重复,直到锁被释放或客户端宕机。
常见分布式锁方案