知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
Redis 实现分布式锁的核心目标是 在分布式系统中确保对共享资源的互斥访问。其设计需解决 锁竞争、死锁预防、容错性 等关键问题。以下是 Redis 分布式锁的完整实现方案及深度分析:
一、基础实现:SETNX + 过期时间
1. 加锁
SET lock_key unique_value NX PX 30000
- NX:仅当键不存在时设置成功(原子性竞争锁)。
- PX 30000:锁自动过期时间(毫秒),避免死锁。
- unique_value:客户端唯一标识(如 UUID),用于安全释放锁。
2. 解锁(Lua 脚本保证原子性)
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
- 脚本作用:只有锁的持有者才能释放锁,避免误删其他客户端的锁。
3. 风险与缺陷
- 锁过期但业务未完成:锁自动释放后,其他客户端可能获取锁,导致并发问题。
- 单点故障:单节点 Redis 崩溃时锁失效。
二、高可用方案:Redlock 算法
Redis 官方推荐的分布式锁算法,适用于多节点 Redis 集群(如 Redis Sentinel 或 Cluster)。
1. 加锁流程
- 获取当前时间(T1)。
- 依次向 N 个独立 Redis 节点请求加锁(使用相同的 Key 和随机值),每个请求设置超时(远小于锁过期时间)。
- 计算加锁总耗时(T2 - T1),仅当在多数节点(≥ N/2 + 1)加锁成功且总耗时小于锁过期时间时,认为加锁成功。
- 锁的实际有效时间 = 初始过期时间 - 加锁总耗时。
2. 解锁流程
向所有节点发送 Lua 解锁脚本(同基础实现)。
3. 配置建议
- 节点数量:至少 5 个(允许最多 2 个节点故障)。
- 锁过期时间:根据业务操作时间调整(如 10~30 秒)。
4. Redlock 争议点
- 时钟漂移问题:若节点时钟不同步,可能导致锁提前失效。
- 性能开销:需与多个节点交互,延迟较高。
三、生产环境最佳实践
1. 锁续期(Watchdog)
- 问题:业务执行时间可能超过锁过期时间。
- 解决方案:启动后台线程定期检查并延长锁持有时间(如每 10 秒续期一次)。
// Java 示例(Redisson 实现) RLock lock = redisson.getLock("lock_key"); lock.lock(30, TimeUnit.SECONDS); // 自动续期
2. 避免锁冲突
- 分段锁:将大 Key 拆分为多个小 Key(如
lock:1、lock:2),降低竞争概率。 - 非阻塞尝试:
若失败,可退避重试或直接放弃。SET lock_key unique_value NX PX 10000
3. 容灾与监控
- Redis 节点高可用:使用 Sentinel 或 Cluster 模式。
- 锁状态监控:
# 检查锁是否存在及剩余生存时间 TTL lock_key
四、与其他分布式锁方案的对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Redis 单节点锁 | 实现简单,性能高(毫秒级) | 单点故障风险 | 非关键业务,允许短暂不一致 |
| Redlock | 高可用,多数节点存活即可工作 | 实现复杂,性能较低(约 100ms) | 金融交易、库存扣减等强一致场景 |
| ZooKeeper 锁 | 强一致性,无过期时间问题 | 性能差(百毫秒级),依赖 ZK 集群 | 对一致性要求极高的系统 |
| 数据库乐观锁/悲观锁/唯一索引 | 无需额外组件 | 高并发下性能骤降 | 低并发、已有数据库集成,没有引入redis、zk的场景 |
五、代码示例(Python + Redlock)
from redlock import RedLock
# 加锁
lock = RedLock("resource_name", ttl=30000) # ttl 单位毫秒
if lock.acquire():
try:
# 执行业务逻辑
print("Do something...")
finally:
# 释放锁
lock.release()
else:
print("Failed to acquire lock")
六、关键问题与解决方案
1. 锁被其他客户端释放
- 原因:客户端 A 的锁过期后,客户端 B 获取锁,此时 A 仍可能误删 B 的锁。
- 解决:解锁时验证
unique_value(如 Lua 脚本)。
2. 锁永久失效
- 原因:Redis 崩溃且未持久化,锁信息丢失。
- 解决:使用 Redlock 多节点部署,或切换为 ZooKeeper/etcd。
3. 业务执行时间不确定
- 解决:动态调整锁过期时间 + Watchdog 续期。
七、总结
- 简单场景:单节点
SETNX + 过期时间 + Lua 解锁即可。 - 高可用场景:使用 Redlock 算法,但需权衡性能与一致性。
- 终极方案:对于强一致性需求,可结合 ZooKeeper 或 数据库事务。
Redis 分布式锁的核心在于 互斥性、容错性 和 避免死锁,合理选择方案需根据业务对性能与一致性的要求。