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

相关推荐
韶博雅1 小时前
oracle中表和列转大写
数据库·oracle
暴躁小师兄数据学院2 小时前
【AI大数据工程师特训笔记】第04讲:PostgreSQL 数据库内置函数详解
大数据·数据库·笔记·ai·语言模型
苏渡苇3 小时前
Spring Cloud Alibaba:将 Sentinel 熔断限流规则持久化到 Nacos 配置中心
数据库·spring boot·mysql·spring cloud·nacos·sentinel·持久化
杨云龙UP3 小时前
Oracle Recycle Bin 回收站详解:DROP TABLE 后还能找回吗?
linux·运维·数据库·sql·mysql·oracle
未来之窗软件服务3 小时前
酒店门锁V10SDK接口VB-幽冥大陆(一百26)—东方仙盟
数据库·酒店门锁·仙盟创梦ide·东方仙盟·东方仙盟sdk·东方仙盟幽冥大陆
tongluowan0074 小时前
Redisson的参数及工作原理
java·redis·lua·分布式锁
墨_风4 小时前
MyBatis时间区间查询异常排查(达梦数据库)
数据库·mybatis·达梦
njsgcs4 小时前
用clip把设计经验变成向量数据库,然后每秒检索可以检查3维模型设计的错误吗
数据库
WiChP4 小时前
【V0.1B10】从零开始的2D游戏引擎开发之路
java·数据库·游戏引擎