续期的无限套娃

续期的无限套娃 scheduleExpirationRenewal最终会调用renewExpirationprivate void renewExpiration() { // 这里的 1/3 是硬编码的规则 // 默认 lockWatchdogTimeout 是 30000ms // 所以每 10000ms 执行一次 Timeout task commandExecutor.getConnectionManager().newTimeout(new TimerTask() { Override public void run(Timeout timeout) throws Exception { // 执行 Lua 脚本把 ttl 重新刷回 30秒 RFutureBoolean future renewExpirationAsync(threadId); future.onComplete((res, e) - { if (res) { // 如果续期成功这就形成了递归调用自己调自己 renewExpiration(); } }); } }, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); }核心逻辑总结三分之一原则每隔锁超时时间的 1/3默认10秒检查一次。无限递归只要检查到锁还在就重置过期时间并注册下一次检查。生死绑定这个任务跑在客户端进程里如果客户端宕机任务停止Redis 里的锁在 30秒 后自动过期。四、我在生产环境踩过的坑避坑实战API 谁都会调但能避开坑的才是老司机。这六个坑都是真金白银换来的教训。 陷阱一好心办坏事 —— 弄死看门狗这是新手最容易犯的错。❌ 错误姿势// 我怕死锁所以强行指定 10秒 过期 lock.lock(10, TimeUnit.SECONDS); // 或者 lock.tryLock(1, 10, TimeUnit.SECONDS);⚠️ 后果Redisson 的看门狗WatchDog机制只有在你未指定锁过期时间时才会生效一旦你手动传了leaseTimeRedisson 就会认为你有自己的想法不再插手。如果你的业务因为数据库卡顿跑了 15秒第 10秒 时锁就会强制过期其他线程长驱直入爆发并发事故。✅ 正确姿势除非你非常确定业务能在指定时间内跑完否则尽量不要传 leaseTime让看门狗帮你自动续期。 陷阱二锁粒度太粗 —— 全服暂停键❌ 错误姿势// 所有订单共用一把锁 RLock lock redisson.getLock(LOCK_ORDER);⚠️ 后果这相当于把高速公路封成了独木桥。不管有多少个用户下单同一时间只能处理一个。性能直接归零。✅ 正确姿势锁的粒度越细越好。只锁那个具体产生竞争的资源 ID。// 只锁这个订单 RLock lock redisson.getLock(order:pay: orderId); 陷阱三解锁的艺术 —— 谁加的锁谁来解❌ 错误姿势try { // 业务逻辑 } finally { lock.unlock(); // 直接解锁 }⚠️ 后果如果业务执行超时锁已经被自动释放了你再去unlock会抛出IllegalMonitorStateException。如果不小心解了别人的锁虽然 Redisson 有 ID 校验防止误删但异常处理依然重要。✅ 正确姿势if (lock.isLocked() lock.isHeldByCurrentThread()) { lock.unlock(); } 陷阱四重入锁的递归噩梦Redisson 的锁虽然是可重入的Reentrant但如果你在递归或嵌套调用中不注意很容易逻辑混乱。❌ 风险代码void methodA() { lock.lock(); try { methodB(); // methodB 里又 lock 了一次 } finally { lock.unlock(); // 只解了一层 } }⚠️ 后果Redis 里的锁计数器Counter如果不归零锁是不会释放的。确保你的加锁次数和解锁次数严格匹配。 陷阱五主从切换的幽灵锁这是 Redis 架构天生的短板。Client A 在Master节点拿到了锁。Master 还没来得及把锁同步给 Slave就宕机了。Slave 升级为新的 Master。Client B 来加锁发现新 Master 上没锁于是也加锁成功。⚠️ 后果A 和 B 同时持有了锁。解法如果你不能容忍这个概率极低请看下文的 RedLock或者转投 Zookeeper。对于 99% 的业务我们选择接受这个风险。五、RedLock 的爱恨情仇有些面试官特别喜欢问 RedLock但在实际工作中它是一个让人爱恨交加的存在。1. 它是为了解决什么解决 Redis 主从集群在 Failover故障转移时可能丢锁的问题。2. 怎么用你需要准备3个或5个完全独立的 Redis 实例不是 Cluster不是 Sentinel就是干干净净的单实例。RLock lock1 redissonInstance1.getLock(lock); RLock lock2 redissonInstance2.getLock(lock); RLock lock3 redissonInstance3.getLock(lock); // 创建红锁 RedissonRedLock lock new RedissonRedLock(lock1, lock2, lock3); try { // 同时向 3个 Redis 申请锁 // 只要有 1.5个 (即2个) 申请成功就算赢 lock.lock(); // 业务逻辑 } finally { lock.unlock(); }3. 灵魂拷问值得吗