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

相关推荐
Omics Pro7 小时前
Cell封面|衰老生物学开源AI工具包
数据库·人工智能·算法·机器学习·自然语言处理
夕除8 小时前
redis--020
redis
IT枫斗者枫哥15 小时前
MyBatis一对多分页:LIMIT 20,为什么凑不齐20个订单?
java·数据库
Y0015 小时前
PostgreSQL 的锁:为什么你的 ALTER TABLE 会卡住,以及怎么查
数据库·postgresql
不好听61315 小时前
缓存三大坑:穿透·击穿·雪崩一次讲透
redis
行百里er15 小时前
Redis 核心数据结构(四)——Set 与 Sorted Set,去重与排名神器
redis·后端
灰原喜欢柯南15 小时前
OceanBase 运维管理知识点——OCP 监控、诊断与变更控制
数据库·oceanbase
码农-00415 小时前
Springboot 集成 Ehcache操作数据库显示SQL语句设置
数据库·spring boot·sql
仍然.15 小时前
Redis---事务
数据库·redis
oradh15 小时前
Oracle 执行计划的阅读方法(二叉树结构来理解和阅读)
数据库·oracle·执行计划的阅读方法