北京时间2026年4月8日
在分布式系统的并发控制中,Redis分布式锁是高频考点,也是生产环境中最易踩坑的技术之一。很多开发者在项目中用过SETNX,也听过Redlock,但真要回答“Redis分布式锁到底怎么实现”“主从架构下锁为什么会失效”“Redlock解决了什么问题”时,往往答不完整甚至答错。本文将由浅入深,带你完整理解Redis分布式锁的核心原理、实现演进和高频面试考点。

一、痛点切入:为什么需要分布式锁?
在单机应用中,使用synchronized或ReentrantLock就能轻松保证线程安全。但在分布式集群中,多个服务实例可能同时操作共享资源——比如秒杀场景下10台服务器同时扣减同一个商品的库存,单机锁根本无法跨实例生效。这就是分布式锁要解决的问题。

来看一个典型的错误实现:
// 错误示范:分步执行SETNX + EXPIRE jedis.setnx("lock:product:001", "1"); // 加锁 jedis.expire("lock:product:001", 30); // 设置过期时间
致命缺陷:如果在执行完SETNX后Redis宕机或网络中断,EXPIRE命令未被执行,锁将永不释放,直接导致死锁。
更隐蔽的问题在于:即使解决了原子性问题,业务处理时间不可控——锁设了30秒过期,但业务逻辑跑了35秒,锁自动释放后其他线程趁虚而入,超卖就发生了。
这些问题的根源,恰恰是理解Redis分布式锁价值的起点。
二、核心概念讲解:分布式锁
分布式锁的本质,是在一个所有节点都能访问的公共外部存储中设置一个排他性标记,确保同一时刻只有一个客户端能操作共享资源-6。
用生活化的场景来理解:就像公司的唯一会议室,谁先拿到钥匙谁就能进去用,用完把钥匙还回去,其他人才能拿。区别在于,分布式锁还需要考虑“用钥匙的人突然失联了怎么办”——这就引出了过期时间的设计。
一个合格的分布式锁必须满足三个核心特性-33:
| 特性 | 含义 | 类比 |
|---|---|---|
| 互斥性 | 同一时刻只有一个客户端持有锁 | 会议室钥匙一次只能给一个人 |
| 无死锁 | 即使客户端崩溃,锁最终也能被释放 | 失联的人不会一直占着钥匙 |
| 容错性 | 部分节点故障时锁服务仍能运作 | 备用钥匙柜能正常使用 |
三、关联概念讲解:SETNX + Lua脚本
SETNX(SET if Not eXists)是Redis实现分布式锁的基础命令——仅在Key不存在时设置值,天然适合互斥场景-21。
但仅有SETNX远远不够。Redis官方推荐的标准加锁方式为:
SET resource_name my_random_value NX PX 30000这条命令原子地完成了三个动作:仅在Key不存在时设置(NX),同时设置30秒自动过期(PX 30000),并绑定一个唯一随机值作为锁的“签名”-13。
为什么要唯一随机值? 看一个血案场景:
客户端A持有锁,但业务处理时间超过了锁的过期时间;锁自动释放,客户端B获取了锁;客户端A完成操作后直接执行DEL——此时删除的是客户端B的锁!
解决这个问题,必须使用Lua脚本来保证“判断+删除”的原子性-13:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end
一句话理解SETNX与Lua脚本的关系:SETNX负责原子地加锁,Lua脚本负责安全地解锁——前者保证互斥,后者防止误删。
四、概念关系与区别总结
| 维度 | SETNX | Lua脚本 |
|---|---|---|
| 作用 | 加锁(获取互斥访问权) | 解锁(安全释放) |
| 原子性 | 单命令天然原子 | 脚本内所有操作原子执行 |
| 核心价值 | 保证互斥性 | 保证解锁时的身份校验安全 |
一句话概括:SETNX负责“锁住门”,Lua脚本负责“用正确钥匙开门”。
五、代码示例演示
完整加锁+解锁实现(Python)
import redis import uuid r = redis.StrictRedis() lock_key = "lock:product:001" client_id = str(uuid.uuid4()) 加锁:原子操作,过期时间30秒 acquired = r.set(lock_key, client_id, nx=True, ex=30) if acquired: try: 临界区代码(如扣减库存) pass finally: 释放锁:Lua脚本校验身份 lua_script = """ if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end """ r.eval(lua_script, 1, lock_key, client_id)
执行流程解析:
生成唯一标识
client_id(UUID)调用
SET key value NX EX 30,原子完成加锁+设过期若返回
True,说明加锁成功,进入临界区释放时执行Lua脚本,仅当当前客户端是锁的持有者时才执行
DEL
对比改进效果:错误实现(分步SETNX+EXPIRE)存在死锁风险;正确实现(原子SET+Lua释放)同时解决了死锁和误删两大核心问题。
六、底层原理/技术支撑点
Redis分布式锁的底层依赖于以下关键技术:
Redis单线程命令处理模型:每个命令天然原子执行,是分布式锁互斥性的基础
Lua脚本机制:在Redis服务端原子执行多条命令,避免竞态条件-
Key过期机制(TTL):自动释放锁,防止客户端崩溃导致的死锁
WatchDog机制(以Redisson为例):后台线程定期检查业务是否完成,动态延长锁的过期时间。默认每隔锁有效期的1/3时间(如锁30秒则每隔10秒)检查一次,未完成则重置为30秒-33
注意:WatchDog仅在调用lock()且未显式指定leaseTime时启动。如果写成lock.lock(10, TimeUnit.SECONDS),看门狗将不会启动-6。
七、进阶:Redlock算法与主从架构的困境
为什么需要Redlock?
Redis主从架构存在一个致命缺陷:异步复制导致锁可能丢失。
客户端A在主节点获取锁,但写入尚未同步到从节点时主节点宕机;从节点被提升为新主节点;客户端B在新主节点成功获取同一资源的锁——两个客户端同时持有了锁,互斥性被破坏-13。
从CAP理论看,Redis主从架构遵循AP(高可用+分区容忍),牺牲了强一致性,这正是主从切换时锁失效的根源-11。
Redlock算法核心思想
Redis官方提出的Redlock算法,通过多个独立的Redis实例来解决单点失效问题:只有在超过半数的Redis节点上成功获取锁,才算加锁成功-11。
标准加锁步骤-51:
获取当前时间戳
依次向N个独立Redis节点(推荐N=5)尝试加锁
每个节点使用
SET key value NX PX原子加锁若超过半数(N/2+1)节点加锁成功,且总耗时 < 锁过期时间,则加锁成功
否则依次释放所有节点上的锁
// Redlock核心逻辑伪代码(Go) quorum := len(clients)/2 + 1 // 5节点 → quorum=3 for _, client := range clients { if client.SetNX(ctx, key, token, ttl).Val() { success++ } if time.Since(start) > ttl/2 { break // 超出安全窗口则放弃 } } return success >= quorum
使用建议:大多数业务场景下不需要Redlock——它要求5个独立Redis节点,多数成功才算拿锁,代价是延迟高、实现复杂。只有在金融、支付等对数据一致性要求极高的场景才值得引入-。
八、高频面试题与参考答案
Q1:Redis分布式锁的实现步骤?
参考答案:
使用
SET key value NX EX seconds原子完成加锁+设过期value必须是全局唯一标识(如UUID)
释放锁时使用Lua脚本校验value后删除
业务耗时不确定时,引入看门狗自动续期机制
Q2:为什么不能用SETNX+EXPIRE两步实现?
参考答案:两步操作非原子,存在死锁风险——若SETNX后Redis宕机或网络中断,EXPIRE未执行,锁将永不释放。
Q3:Redlock解决了什么问题?有哪些争议?
参考答案:Redlock解决Redis主从架构下异步复制导致锁丢失的问题,通过多个独立Redis节点和多数派投票保障互斥性。争议点在于:Redlock依赖时钟同步假设,网络分区或时钟漂移时仍可能失效,且实现复杂度远高于单实例方案,多数业务场景不需要。
Q4:Redisson相比手写SETNX有哪些优势?
参考答案:
可重入:同一线程可重复获取同一锁,使用Hash结构存储重入次数
自动续期:看门狗机制自动延长锁过期时间
原子性保障:所有操作通过Lua脚本实现
工程完备:内置锁语义、公平锁、红锁等多种模式
九、结尾总结
| 阶段 | 核心技术 | 解决的问题 |
|---|---|---|
| 基础 | SET NX PX + Lua | 原子加锁、防死锁、防误删 |
| 进阶 | Redisson + WatchDog | 自动续期、可重入 |
| 高可用 | Redlock算法 | 主从切换时锁丢失 |
重点回顾:
加锁必须原子(
SET NX PX),释放必须校验身份(Lua)业务处理时间 > 锁过期时间 → 超卖风险 → 需要续期机制
主从异步复制 → 锁可能丢失 → 高一致性场景考虑Redlock
易错点:锁粒度不够精细、直接用固定字符串当锁key、释放锁时忘记校验value直接DEL。
下篇预告:Redisson源码深度剖析——看门狗机制的Lua实现与分布式锁的集群适配。
参考资料:Redis官方文档、JavaGuide分布式锁总结、阿里云开发者社区、CSDN技术博客等。