1. 加锁机制
核心流程
- 尝试加锁
- 客户端通过执行 Lua脚本 向Redis发送加锁请求,保证原子性操作。
- 锁存储结构
- Redis中存储的锁是一个 Hash结构:
Key
:锁名称(唯一标识)Field
:客户端ID(UUID + 线程ID)Value
:重入次数(支持可重入锁)
- 锁超时时间
Lua脚本
if (redis.call('exists', KEYS) == 0) then
redis.call('hset', KEYS, ARGV, 1);
redis.call('pexpire', KEYS, ARGV);
return nil;
end;
if (redis.call('hexists', KEYS, ARGV) == 1) then
redis.call('hincrby', KEYS, ARGV, 1);
redis.call('pexpire', KEYS, ARGV);
return nil;
end;
return redis.call('pttl', KEYS);
2. 锁自动续期(Watchdog)
看门狗机制
- 问题:业务执行时间超过锁超时时间,导致锁提前释放。
- 解决方案:
- 客户端加锁成功后,启动后台线程(Watchdog)。
- 每隔 超时时间的1/3(默认10秒) 自动续期锁。
- 业务完成后,Watchdog自动停止。
续期Lua脚本
if (redis.call('hexists', KEYS, ARGV) == 1) then
redis.call('pexpire', KEYS, ARGV);
return 1;
end;
return 0;
3. 释放锁流程
释放逻辑
- 检查当前线程是否是锁的持有者。
- 如果是,重入次数减1;若重入次数为0,删除锁。
- 取消Watchdog续期任务。
Lua脚本
if (redis.call('hexists', KEYS, ARGV) == 0) then
return nil;
end;
local counter = redis.call('hincrby', KEYS, ARGV, -1);
if (counter > 0) then
redis.call('pexpire', KEYS, ARGV);
return 0;
else
redis.call('del', KEYS);
redis.call('publish', KEYS, ARGV);
return 1;
end;
return nil;
4. 高可用性设计
Redis部署模式
模式 | 特点 |
---|
单节点 | 简单,适用于低并发场景 |
主从模式 | 主节点宕机时可能丢失锁(异步复制) |
哨兵模式 | 自动切换主节点,提高可用性 |
集群模式 | 通过哈希槽分配锁,支持高并发和高可用 |
红锁(RedLock)
- 客户端向多个Redis节点(≥3)请求加锁。
- 多数节点(N/2+1)加锁成功时,视为成功。
- 锁有效时间 = 锁超时时间 - 获取锁总耗时。
5. 核心优势
- ✅ 可重入锁:同一线程可多次获取同一锁。
- ✅ 自动续期:通过Watchdog避免锁超时释放。
- ✅ 高可用:支持多种Redis部署模式。
- ✅ 锁类型丰富:公平锁、非公平锁(
RLock
)。
6. 注意事项
- 网络延迟:可能导致锁超时失效。
- 时钟漂移:需使用NTP同步时钟。
- 业务超时:依赖Watchdog,需避免业务无限阻塞。
7. 业务逻辑无限阻塞处理方案
代码层面预防
措施 | 实现方式 |
---|
业务逻辑超时控制 | 使用Future 、线程中断机制 |
合理设置锁超时时间 | 不依赖WatchDog,直接指定超时时间 |
应急处理
措施 | 工具/技术 |
---|
监控告警 + 手动释放 | Redis CLI、Prometheus监控 |
重启服务 | Kubernetes健康检查(livenessProbe ) |