Redis分布式锁详解
分布式锁是在分布式系统中实现互斥访问共享资源的重要机制。Redis因其高性能和原子性操作特性,常被用来实现分布式锁。
一、基础实现方案
1. SETNX + EXPIRE方案(基本版)
# 加锁
SETNX lock_key unique_value # 设置唯一标识
EXPIRE lock_key 10 # 设置过期时间# 解锁
DEL lock_key # 删除键
问题:
- 非原子性操作(SETNX和EXPIRE分开执行可能失败)
- 可能误删其他客户端的锁
2. SET扩展命令方案(推荐)
# 原子性加锁
SET lock_key unique_value NX PX 10000 # NX表示不存在才设置,PX设置毫秒级过期时间# 解锁(Lua脚本保证原子性)
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end
优势:
- 原子性加锁
- 设置过期时间防止死锁
- 通过唯一值避免误删
二、Redlock算法(多节点版)
当需要更高可靠性时,Redis作者提出的Redlock算法:
- 获取当前时间(毫秒)
- 依次尝试从N个独立的Redis节点获取锁
- 计算获取锁总耗时(小于锁过期时间)
- 当从多数节点(N/2+1)获取成功,且总耗时小于锁有效时间,才认为加锁成功
- 若失败,向所有节点发起解锁请求
三、Java实现示例(Redisson)
// 1. 获取锁对象
RLock lock = redissonClient.getLock("myLock");try {// 2. 尝试加锁(参数:等待时间,锁自动释放时间,时间单位)boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);if (isLocked) {// 3. 执行业务代码doBusiness();}
} finally {// 4. 释放锁lock.unlock();
}
四、关键问题与解决方案
1. 锁续期(Watchdog机制)
问题:业务执行时间超过锁过期时间
方案:Redisson的看门狗会定期(默认10秒)检查并延长锁时间
2. 可重入性
方案:使用计数器记录重入次数(Redisson已实现)
3. 锁等待与公平性
方案:
- 实现锁等待队列
- 使用Redis的List结构模拟队列
4. 主从切换问题
问题:主节点崩溃可能导致锁丢失
方案:
- 使用Redlock多节点方案
- 或使用Redis的WAIT命令确保数据同步
五、生产环境建议
- 锁命名规范:
业务:资源:操作
如order:123:pay
- 超时时间设置:
- 不宜过短(业务未完成锁已释放)
- 不宜过长(系统故障时恢复慢)
- 监控指标:
- 锁获取成功率
- 平均持有时间
- 等待队列长度
- 降级方案:Redis不可用时切换本地锁或数据库锁
六、与其他方案对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
Redis锁 | 性能高、实现简单 | 可靠性依赖Redis | 大多数分布式场景 |
Zookeeper锁 | 可靠性高 | 性能较低 | 强一致性要求场景 |
数据库锁 | 无需额外组件 | 性能差、有死锁风险 | 简单场景 |
七、最佳实践
- 始终为锁设置合理的过期时间
- 使用唯一标识(UUID/线程ID)作为锁值
- 释放锁前验证持有者身份
- 考虑使用成熟的客户端(如Redisson)而非自己实现
- 重要业务实现锁续期机制
Redis分布式锁在大多数场景下能很好工作,但对于对可靠性要求极高的场景,建议考虑Zookeeper或etcd等强一致性协调服务实现的分布式锁。