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

相关推荐
不像程序员的程序媛7 小时前
系统cpu内存负载资源分析
运维·服务器·数据库
Jane Chiu8 小时前
Oracle DBMS_REDEFINITION(在线重定义)
数据库·oracle·分区表
全栈前端老曹9 小时前
【MongoDB】安全与权限管理 —— 用户认证、角色权限、SSL 加密
前端·javascript·数据库·安全·mongodb·nosql·ssl
怕孤单的草丛9 小时前
缓存管理面临的主要问题
java·数据库·缓存
我会尽全力 乐观而坚强10 小时前
MySQL零基础入门(二)
数据库·mysql·adb
kali-Myon11 小时前
某校园门禁系统高危 SQL 注入漏洞挖掘复盘
数据库·sql·安全·web安全
程序员在囧途12 小时前
likeadmin-api API 中转站怎么做统一报价?从 /pricing、/user/balance 到 task_id 回执的落地方法
运维·服务器·数据库·likeadmin-api·api中转站·token计费
龙石数据13 小时前
MySQL 全量同步到 Hive 怎么做?三步配置教程
数据库·hive·mysql·数据治理·数据中台
程序员在囧途13 小时前
likeadmin-api API 算力超市怎么做供应商切换?统一鉴权、task_id 和 callback_url 才能稳交付
java·服务器·数据库·开放api·likeadmin-api·api算力超市