题目
如何基于Redis实现高可用的分布式锁并解决锁失效问题?
信息
- 类型:问答
- 难度:⭐⭐
考点
分布式锁原理,Redis实现方案,锁续期机制,高可用设计,原子性操作
快速回答
实现高可用Redis分布式锁的核心要点:
- 使用
SET key random_value NX PX 30000原子命令加锁 - 通过Lua脚本保证解锁操作的原子性
- 引入看门狗线程自动续期锁过期时间
- 使用Redlock算法应对Redis主从故障场景
- 设置唯一随机值防止误删其他客户端锁
一、核心原理
分布式锁需满足:互斥性、防死锁、容错性。Redis实现依赖:
- SET NX PX:原子性设置键值+过期时间
- Lua脚本:保证解锁操作的原子性
- Redlock:在多个Redis实例上获取锁解决单点故障
二、代码实现示例
1. 加锁实现
public boolean tryLock(Jedis jedis, String lockKey, String clientId, int expireTime) {
String result = jedis.set(lockKey, clientId, "NX", "PX", expireTime);
return "OK".equals(result);
}2. 解锁实现(Lua脚本)
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end3. 锁续期(看门狗)
// 后台线程定期续期
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
if (jedis.get(lockKey).equals(clientId)) {
jedis.pexpire(lockKey, expireTime); // 重置过期时间
}
}, 0, expireTime / 3, TimeUnit.MILLISECONDS); // 1/3周期续期三、最佳实践
- 唯一客户端ID:使用UUID等随机值,避免误删其他客户端锁
- 锁续期周期:设置续期间隔 ≤ 过期时间的1/3
- 超时控制:加锁操作设置网络超时时间
- Redlock使用场景:在5个独立Redis主节点上获取锁,当≥3个成功时视为加锁成功
四、常见错误
- 错误1:解锁时未校验客户端ID → 导致误删其他客户端锁
- 错误2:未设置锁超时 → 客户端崩溃导致死锁
- 错误3:业务执行时间超过锁过期时间 → 锁提前释放
- 错误4:单点Redis故障时未启用Redlock → 锁状态不一致
五、扩展知识
- Redlock算法流程:
- 获取当前毫秒级时间戳T1
- 在N个实例上顺序执行加锁命令
- 计算加锁总耗时 = T2 - T1
- 当成功数≥N/2+1 且 耗时 < 锁有效时间时加锁成功
- 其他方案对比:
- Zookeeper:通过临时顺序节点实现,强一致性但性能较低
- etcd:基于Raft协议,适合CP场景
- 锁粒度优化:对非冲突资源使用细粒度锁提升并发度