Redis 分布式锁详解:实现与缺陷

一、问题背景

在分布式场景中,多个进程/服务器独立运行,访问临界资源时需要互斥访问。分布式锁用于保证同一时刻只有一个进程能访问共享资源。

复制代码
单机环境:                        分布式环境:
+-------------+                  +-------------+
| 进程A       |                  |  服务器A    |
|  进程B      |  临界资源         |  服务器B    |
|  进程C      | <--------->      |  服务器C    |
+-------------+                  +-------------+
     |                                  |
   进程锁                              分布式锁(Redis)

二、分布式锁的核心要求

2.1 互斥性

锁只能被一个对象持有。 这是分布式锁的基本语义。

2.2 高可用

锁本身也是一个资源,提供锁的服务宕机后,其他服务仍能继续获取锁。

2.3 同一对象操作

获取锁和释放锁必须是同一对象,避免误删他人持有的锁:

复制代码
问题场景:
1. A 获取锁 lock = uuid_A
2. A 因故超时退出
3. B 获取锁 lock = uuid_B
4. A 恢复后释放锁(DEL lock)<- 错误!释放了 B 的锁
2.4 锁超时

避免持有锁的进程异常退出后锁无法释放,导致死锁。


三、Redis 分布式锁实现

3.1 基础实现:SETNX
lua 复制代码
-- 获取锁
SETNX lock uuid

-- 释放锁
DEL lock

问题: B 也可以 DEL A 的锁,存在误删风险。

3.2 改进实现:UUID 标识
lua 复制代码
-- 获取锁
SET lock uuid NX EX 10

-- 释放锁(需判断 UUID)
if redis.call("GET", lock) == uuid then
    redis.call("DEL", lock)
    return 1
else
    return 0
end

关键设计: 用 UUID 标识锁持有者,释放时先判断 UUID 是否匹配。

3.3 Redis 高可用方案
模式 说明
哨兵模式 主从自动切换,保证高可用
Cluster 模式 分片存储,支持大规模集群

四、Redis 分布式锁的缺陷

4.1 主从异步复制问题

Redis 主从之间采用异步复制:

复制代码
主节点:写入 lock=uuid    ->  立即返回成功
从节点:同步 lock=uuid   ->  可能延迟

问题: 主节点宕机,从节点晋升,但锁数据未同步,导致锁失效。

复制代码
场景:
1. A 向主节点写入 lock=uuid_A,成功
2. 主节点宕机,锁数据未同步到从节点
3. 从节点晋升为新主节点
4. B 向新主节点写入 lock=uuid_B,成功
5. A 和 B 同时持有锁,互斥性被破坏

这是 Redis 分布式锁最大的缺陷:不是强一致的。

4.2 无法实现公平锁

非公平锁: A 释放锁时,BCD 同时去争抢,先到先得。

复制代码
时间线:
T1: A 获取锁
T2: A 释放锁
T3: B,C,D 同时争抢 -> 随机选择胜者

公平锁: 按请求顺序排队,FIFO。

Redis 无法实现公平锁,因为:

  • 不支持队列排队
  • 锁释放时无法主动通知等待者
4.3 锁超时问题

Redis 分布式锁依赖 TTL 超时释放,但存在:

问题1:超时时间难以确定

  • 设太长:进程崩溃后锁长时间不释放
  • 设太短:业务未执行完就自动释放

问题2:业务执行时间不确定

复制代码
客户端流程:
1. 获取锁(expire=10s)
2. 执行业务(预计 8s)
3. 业务异常(实际 15s)
4. 锁已自动释放,其他进程获取锁
5. 原进程继续执行,可能破坏数据

五、Redis vs 其他分布式锁方案对比

维度 Redis MySQL ZooKeeper etcd
性能 高(内存) 低(磁盘) 中 中
高可用 支持(主从) 支持(主从) 支持 支持
一致性 弱(异步复制) 强(同步复制) 强(ZAB协议) 强(Raft协议)
公平锁 不支持 支持 支持 支持
锁粒度控制 TTL 行锁/表锁 临时节点 租约机制
实现复杂度 低 低 中 中

六、面试追问 FAQ

问题 回答要点
Q: Redis 分布式锁为什么不支持公平锁? Redis 不支持队列机制,锁释放后所有等待者同时争抢
Q: 如何解决 Redis 主从复制导致的锁丢失? RedLock 算法:向 N 个 Redis 实例获取锁,超过半数成功才算获取成功
Q: 锁超时时间怎么设置? 根据业务预估执行时间 + 一定余量,或使用看门狗机制续期
Q: 如何防止误删他人锁? 释放前先 GET 判断 UUID,Lua 脚本保证原子性
Q: RedLock 有什么问题? 需要向多个 Redis 实例操作,性能下降,且在时钟漂移场景下不可靠

七、相关题目

题目 考察点
Redis 分布式锁如何保证原子性? Lua 脚本
Redis 分布式锁的超时时间怎么设计? 业务预估 + 续期机制
如何实现可重入的分布式锁? 锁记录中增加持有次数和线程标识
RedLock 算法的原理? 多节点多数投票

八、总结

特性 Redis 分布式锁
互斥性 通过 SETNX 保证
高可用 支持哨兵/Cluster
同一对象操作 通过 UUID 标识
锁超时 通过 EXPIRE 实现
强一致性 异步复制可能丢锁
公平锁 不支持
性能 高

核心结论: Redis 分布式锁实现简单、性能高,但在主从异步复制场景下存在丢锁风险,不适合对一致性要求极高的场景。对于需要强一致性的场景,建议使用 Zookeeper 或 etcd。


根据零声教育教学写作https://github.com/0voice

相关推荐
草莓熊Lotso2 小时前
【C++ 基于微服务的即时通讯系统】etcd + brpc 从安装到服务注册发现全链路实战
网络·数据库·c++·redis·微服务·etcd
Lethehong8 小时前
Oracle 加个字段,目标库就报错?KFS 用“先拆后组“打通异构 DDL
数据库·oracle
Web极客码8 小时前
锁定真正的磁盘卡顿:我用 OpenCode 配合 eBPF 诊断 NVMe 队列深度延迟与 I/O 写放大
数据库·oracle
lisanmengmeng8 小时前
Ganglia分布式集群监控系统安装手册
分布式
cspttty9 小时前
2027用户研究求职准备:应用统计学如何补齐研究设计与业务表达
大数据·数据库
我不会起名字3229 小时前
Redis 突然只能读不能写:MISCONF 报错的 3 类触发条件与 4 步恢复
运维·redis·持久化·排查·misconf
roman_日积跬步-终至千里10 小时前
【数据Agent论文·查询与语义|05】Spider:为什么换一个数据库,模型就不会查了?
数据库·数据分析
猫咪很可爱10 小时前
Oracle RAC 启动失败排查:私网通信与内核参数优化实战
运维·数据库·oracle
我不会起名字32210 小时前
Redis 从 0 到 1(九):主库宕机 30 秒自动切主——1 主 2 从 3 哨兵 + Go 客户端
redis·docker·sentinel·高可用·哨兵
XLYcmy10 小时前
《数字经济赋能城市低碳治理的路径研究检索报告》审阅与修改指导意见——基于完整检索报告体例的完整性诊断、学术规范核查与方法论纠偏 上
数据库·ai·chatgpt·文心一言·检索·知网·豆包