高 困难
Redisson分布式锁实现#
一句话答案#
Redisson 用 Lua 脚本实现可重入锁,Watch Dog 每 10s 自动续期 30s 防止业务未完成锁过期。
核心要点
Redisson 加锁的 Lua 脚本:
-- tryLockInnerAsync 中的加锁脚本(经典写法;新版 Redisson 把前两个分支合并成
-- if exists==0 or hexists==1 then hincrby + pexpire,逻辑等价)
if (redis.call('exists', KEYS[1]) == 0) then
-- 锁不存在,创建并初始化(Hash 结构:key=锁名, field=客户端UUID:threadId, value=重入次数)
redis.call('hset', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil; -- 加锁成功返回 nil
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then
-- 同一线程重入,计数+1
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil; -- 重入成功返回 nil
end;
return redis.call('pttl', KEYS[1]); -- 加锁失败,返回剩余过期时间luaLua 脚本保证并发安全的原因:
- Redis 是单线程执行命令,Lua 脚本在 Redis 内部是原子执行的
- 执行 Lua 脚本期间,Redis 不会执行其他客户端的命令
- 因此 “判断 + 设置” 整个操作是原子的,不会有竞态条件
Watch Dog(看门狗)续期机制:
加锁成功后(默认锁超时 30s):
→ 启动一个定时任务(每 30s/3 = 10s 执行一次)
→ 用 Lua 检查锁 Hash 里是否还有本客户端的 UUID:threadId 字段(不检查线程是否存活)
→ 有 → 重新设置过期时间为 30s(续期)
→ 没有 → 停止续期任务
当持锁线程正常执行完毕并释放锁后:
→ 取消续期任务
当持锁线程宕机:
→ JVM 进程退出,续期任务停止
→ 锁在 30s 后自动过期,其他线程可以获取plaintext面试回答(2分钟版)
Redisson 分布式锁的核心是 Lua 脚本加锁加上 Watch Dog 自动续期。加锁时通过一段 Lua 脚本在 Redis 中原子执行:先判断锁是否存在,不存在就用 Hash 结构创建(field 是”客户端 UUID:线程 ID”,value 是重入次数),设置过期时间;如果锁已存在且是同一线程,重入次数加一并续期。Lua 脚本之所以能保证并发安全,是因为 Redis 单线程执行命令,脚本运行期间不会穿插其他客户端请求,“判断+设置”整个操作是原子的。Watch Dog 看门狗机制解决了锁过期但业务未完成的问题:加锁成功后默认过期时间 30 秒,每 10 秒续期一次重新设为 30 秒;当持锁线程正常释放锁或 JVM 宕机时续期任务停止,锁到期自动释放。需要注意 Redisson 在 Redis 主从架构下仍有缺陷:主库加锁成功但未同步到从库就宕机,从库升主后锁丢失,这个问题 RedLock 方案试图解决但争议较大。
追问与易错
追问方向:
- “Watch Dog 会无限续期吗?”→ 只要客户端进程存活且锁没被 unlock 就会一直续期——续期只检查 Redis 里的锁字段是否还在,不检查持锁线程是否存活,所以忘了 unlock(包括线程异常退出没走 finally)会导致锁被永久续期;JVM 宕机后续期任务(Netty TimerTask)随进程终止,锁在 30s(lockWatchdogTimeout)后自动过期释放
- “锁过期时间设多少?”→ 如果使用 Watch Dog 则不需要手动设置,默认 30s 自动续期;如果手动指定过期时间则 Watch Dog 不启动,需确保过期时间大于业务最长执行时间并留余量
- “RedLock 还推荐吗?”→ 争议较大,Martin Kleppmann 指出其依赖系统时钟假设不可靠、且缺少 fencing token;Redis 作者 Antirez 进行了反驳但未完全消除质疑。Redisson 已把 RedissonRedLock 和 getRedLock() 标记为 @Deprecated,官方建议改用 getLock() 或带 fencing token 的 getFencedLock();金融等强一致场景建议用 ZooKeeper/etcd 分布式锁或在存储侧校验 fencing token
易错点:
- ❌ 用了 Redisson 就万事大吉——主从切换仍可能丢锁
- ❌ Watch Dog 是定时线程——是 Netty TimerTask