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

相关推荐
ClouGence4 小时前
Oracle CDC 架构优化:从主库直连到 DataGuard 备库同步
数据库·后端·oracle
无响应de神6 小时前
三、用户与权限管理
数据库·mysql
小小工匠1 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
麦聪聊数据1 天前
数据服务化时代:企业数据能力输出的核心路径
数据库
shushangyun_1 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化
DARLING Zero two♡1 天前
【MySQL数据库】数据类型与表约束
数据库·mysql
曹牧1 天前
Oracle EXPLAIN PLAN
数据库·oracle
BD_Marathon1 天前
SQL学习指南——视图
数据库·sql
活宝小娜1 天前
mysql详细安装教程
数据库·mysql·adb
贤时间1 天前
codex 助力oracle ebs 开发
数据库·oracle