news 2026/4/3 3:12:44

Redis 中的锁:核心实现、类型与最佳实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis 中的锁:核心实现、类型与最佳实践

Redis 中的锁:核心实现、类型与最佳实践

文章目录

  • Redis 中的锁:核心实现、类型与最佳实践
    • 一、Redis 锁的核心基础:原子性与过期机制
      • 核心基础命令
    • 二、Redis 锁的主要类型
      • 类型 1:单机版 Redis 锁(基础版)
        • 适用场景
        • 核心实现逻辑
        • 核心代码(伪代码)
        • 优缺点
      • 类型 2:Redis 分布式锁(Redlock 红锁,Redis 官方推荐)
        • 适用场景
        • 核心设计思想
        • 核心实现步骤(以 5 个独立 Redis 节点为例)
        • 优缺点
        • 注意事项
      • 类型 3:Redis 可重入锁(基于 Redisson 实现)
        • 适用场景
        • 核心实现(以 Redisson 为例)
        • 核心代码(Redisson 可重入锁)
        • 核心优势
      • 类型 4:Redis 读写锁(RReadWriteLock)
        • 适用场景
        • 核心规则(基于 Redisson)
        • 核心代码(Redisson 读写锁)
    • 三、Redis 锁的常见问题与解决方案
      • 问题 1:死锁
      • 问题 2:锁的误删
      • 问题 3:锁提前释放
      • 问题 4:竞态条件
      • 问题 5:Redis 主从同步延迟导致的锁失效
      • 问题 6:锁的粒度太大
    • 四、Redis 锁的选型建议
    • 五、Redis 锁与其他分布式锁的对比
    • 六、Redis 锁的最佳实践
    • 总结

Redis 作为高性能的内存键值数据库,凭借单线程原子性高读写速度丰富的命令集,成为分布式系统中实现分布式锁的主流方案。Redis 锁主要解决分布式环境下的资源竞争问题,保证多个服务 / 节点对同一资源的操作互斥性,核心分为单机版 Redis 锁分布式 Redis 锁(适配 Redis 集群 / 主从架构),同时衍生出多种优化实现。

一、Redis 锁的核心基础:原子性与过期机制

Redis 实现锁的核心依赖两个特性,也是区别于普通内存锁的关键:

  1. 命令原子性:Redis 单线程执行命令,且提供SETNXSET带多参数等原子命令,避免锁的 “加锁 - 判空” 操作出现竞态条件(比如 A 线程判空后,B 线程同时完成加锁)。
  2. 过期自动释放:为锁设置过期时间,避免持有锁的节点因宕机、网络异常等原因无法手动释放锁,导致死锁

核心基础命令

Redis 锁的实现围绕以下核心命令展开,其中 **SET多参数命令 ** 是目前最优的基础加锁命令:

命令作用适用场景缺点
SETNX key value仅当 key 不存在时设置值,成功返回 1,失败返回 0早期单机锁加锁无法原子性设置过期时间,加锁和设过期分两步会出现死锁风险
EXPIRE key seconds为 key 设置过期时间配合 SETNX 设过期非原子操作,与 SETNX 配合时可能出现 “加锁成功但设过期失败”
SET key value NX EX seconds原子性完成:NX(仅不存在时设置)+ EX(设过期秒数),PX 为毫秒主流加锁方式无核心缺点,是单机锁的标准加锁命令
DEL key删除 key,释放锁手动释放锁若误删其他节点持有的锁,会导致锁失效(需加锁标识
GET key获取锁的 value验证锁的持有者单独使用非原子,需配合GETSET/EVAL实现原子校验 + 释放
GETSET key newvalue原子性获取旧值并设置新值锁的过期续约(看门狗)略复杂,需配合过期时间使用
EVAL script key [key...] arg [arg...]执行 Lua 脚本复杂原子操作(如校验 + 释放)需编写 Lua 脚本,无原生命令简洁

关键结论禁止单独使用SETNX,必须使用SET key value NX EX/PX实现原子加锁 + 设过期,这是 Redis 锁的基础规范。

原因:SETNX加锁与设过期非原子操作,易导致死锁

SETNXEXPIRE是两个独立的 Redis 命令,无法保证原子性。单独用SETNX加锁会出现极端的异常场景:

线程 A 执行SETNX成功(加锁),但还没来得及执行EXPIRE(设过期),就因为机器宕机、网络中断、进程被杀等原因终止;

此时lock_key被创建但没有过期时间,成为 “永久锁”;

其他所有线程都无法通过SETNX获取该锁,导致死锁,资源永远无法被释放。

这种场景在分布式系统中无法避免(比如节点重启、网络抖动),一旦发生就会导致业务阻塞,是生产环境的重大隐患。

二、Redis 锁的主要类型

根据 Redis 的部署架构和实现复杂度,Redis 锁分为单机版分布式版官方标准化版,适配从单节点到高可用集群的不同场景,安全性和实现复杂度依次提升。

类型 1:单机版 Redis 锁(基础版)

适用场景

Redis 单节点部署,无主从 / 集群,适用于低并发、对高可用要求不高的场景(如小型单体应用的分布式部署)。

核心实现逻辑

加锁:使用SET lock_key 唯一标识 NX EX 30(30 为过期时间,单位秒),唯一标识(如 UUID + 节点 ID)用于验证锁的持有者,避免误删。

解锁:先校验锁的 value 是否为当前节点的唯一标识,再删除 key,必须通过 Lua 脚本实现原子性(否则校验和删除分两步会出现误删)。

重试:加锁失败时,可通过自旋(循环重试)或阻塞等待实现重入。

核心代码(伪代码)
// 加锁StringlockKey="resource:lock";StringlockValue=UUID.randomUUID().toString()+"-"+nodeId;// NX:仅不存在时加锁,PX 30000:过期30秒,原子操作BooleanlockSuccess=redisTemplate.opsForValue().setIfAbsent(lockKey,lockValue,30,TimeUnit.SECONDS);if(!lockSuccess){return"加锁失败,资源被占用";}// 业务逻辑try{doBusiness();}finally{// 解锁:Lua脚本保证原子校验+删除StringluaScript="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";redisTemplate.execute(newDefaultRedisScript<>(luaScript,Long.class),Arrays.asList(lockKey),lockValue);}
优缺点
  • 优点:实现简单、性能高,无额外组件依赖;
  • 缺点:Redis 单节点宕机后,整个锁服务不可用;不支持重入(需额外扩展)。

类型 2:Redis 分布式锁(Redlock 红锁,Redis 官方推荐)

适用场景

Redis 主从 / 哨兵 / 集群部署,对锁的高可用要求高(如电商库存扣减、订单创建等核心业务),解决单机版锁的 “单点故障” 问题。

核心设计思想

由 Redis 作者 Antirez 提出,基于多个独立的 Redis 主节点(无主从关系,通常 3/5 个)实现分布式锁,要求在超过半数的节点上成功加锁,才认为整体加锁成功;解锁时需删除所有节点的锁。

核心实现步骤(以 5 个独立 Redis 节点为例)
  1. 生成唯一标识:同单机版,用于标识锁持有者;
  2. 逐个加锁:依次向 5 个 Redis 节点发送SET lock_key 唯一标识 NX EX 超时时间命令,设置统一的加锁超时时间(远小于锁的过期时间,避免某个节点宕机导致阻塞);
  3. 判断加锁成功:若超过半数节点(≥3 个)加锁成功,且总耗时≤锁的过期时间,则加锁成功;否则,立即向所有节点发起解锁,加锁失败;
  4. 执行业务:加锁成功后执行互斥业务;
  5. 统一解锁:向所有 5 个节点发送解锁命令(Lua 脚本),无论节点是否加锁成功。
优缺点
  • 优点:解决 Redis 单点故障,高可用;锁的安全性远高于单机版;
  • 缺点:实现复杂,需维护多个独立 Redis 节点;性能比单机版低(需逐个节点加锁);存在时钟漂移风险(节点间时间不同步可能导致锁提前过期)。
注意事项
  • 红锁要求 Redis 节点完全独立,不能是主从 / 哨兵架构(主从同步存在延迟,主节点宕机后从节点升主,可能出现多个节点持有锁);
  • 加锁超时时间必须远小于锁的过期时间(建议为锁过期时间的 1/5~1/3),避免网络延迟导致总耗时过长。

类型 3:Redis 可重入锁(基于 Redisson 实现)

适用场景

需要锁重入的业务(如方法 A 加锁后,调用的方法 B 也需要对同一资源加锁,无需重复加锁),是对基础 Redis 锁的功能扩展,原生 Redis 不支持重入,需通过第三方框架实现。

核心实现(以 Redisson 为例)

Redisson 是 Redis 的 Java 客户端,基于 Redis 实现了可重入锁、公平锁、读写锁等多种分布式锁,底层通过Lua 脚本Hash 结构实现重入:

  1. 加锁时,用 Hash 结构存储锁key -> {持有者标识: 重入次数}
  2. 首次加锁:创建 Hash,重入次数设为 1,同时设置过期时间;
  3. 重入加锁:校验持有者标识为当前节点,原子性将重入次数 + 1;
  4. 解锁:校验持有者标识,重入次数 - 1,若次数为 0 则删除 Hash(释放锁),否则仅更新次数;
  5. 看门狗机制:若业务未执行完成,锁的过期时间将到期,Redisson 会通过定时任务自动续约锁的过期时间(默认每 10 秒续约,锁默认过期 30 秒),避免锁提前释放。
核心代码(Redisson 可重入锁)
// 初始化Redisson客户端Configconfig=newConfig();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClientredisson=Redisson.create(config);// 获取可重入锁RLocklock=redisson.getLock("resource:lock");// 加锁(可设置过期时间,无参则启用看门狗)lock.lock(30,TimeUnit.SECONDS);try{doBusiness();// 方法内重入加锁,无需等待reenterMethod(lock);}finally{// 解锁,重入时会递减次数,次数为0则释放锁lock.unlock();}// 重入方法publicvoidreenterMethod(RLocklock){lock.lock();try{doSubBusiness();}finally{lock.unlock();}}
核心优势
  • 原生支持可重入、公平锁、读写锁,满足复杂业务需求;
  • 内置看门狗机制,自动续约锁,无需手动设置过期时间;
  • 适配 Redis 单机、主从、集群、哨兵等所有部署架构;
  • 提供阻塞、非阻塞、自旋等多种加锁方式。

类型 4:Redis 读写锁(RReadWriteLock)

适用场景

读多写少的资源竞争场景(如商品详情页缓存、文章内容查询),实现读共享、写互斥,提升并发量(若用普通互斥锁,读操作也会互斥,降低性能)。

核心规则(基于 Redisson)
  1. 读锁:多个节点可同时获取读锁,无互斥;若已有写锁,则读锁获取失败;
  2. 写锁:排他锁,仅一个节点可获取;若已有读锁 / 写锁,则写锁获取失败;
  3. 锁升级:不支持(读锁不能直接升级为写锁,需先释放读锁);
  4. 锁降级:支持(写锁可降级为读锁,无需释放写锁,直接获取读锁)。
核心代码(Redisson 读写锁)
RReadWriteLockrwLock=redisson.getReadWriteLock("resource:rwlock");// 获取读锁RLockreadLock=rwLock.readLock();// 获取写锁RLockwriteLock=rwLock.writeLock();// 写操作:排他锁writeLock.lock(30,TimeUnit.SECONDS);try{// 更新资源,如修改商品库存updateResource();}finally{writeLock.unlock();}// 读操作:共享锁readLock.lock(30,TimeUnit.SECONDS);try{// 查询资源,如查询商品库存getResource();}finally{readLock.unlock();}

三、Redis 锁的常见问题与解决方案

Redis 锁的实现中,若忽略细节,会出现死锁、误删、锁提前释放、并发安全等问题,以下是核心问题及通用解决方案:

问题 1:死锁

原因:持有锁的节点宕机 / 网络异常,无法手动释放锁,且锁未设置过期时间。

解决方案

  1. 加锁时必须原子性设置过期时间SET NX EX/PX),即使节点宕机,Redis 也会自动释放锁;
  2. 高可用场景下使用 Redlock,避免单节点宕机导致锁无法释放;
  3. 业务侧增加兜底任务,清理过期未释放的异常锁。

问题 2:锁的误删

原因:节点 A 的锁因过期自动释放,节点 B 获取锁,此时节点 A 执行完业务,直接DEL锁,误删节点 B 的锁。

解决方案

  1. 加锁时设置唯一的锁标识(如 UUID + 节点 ID / 线程 ID),解锁前先校验标识是否为当前节点所有;
  2. 解锁操作必须原子性(通过 Lua 脚本实现 “校验 + 删除”),禁止分两步执行GETDEL

问题 3:锁提前释放

原因:业务执行时间超过锁的过期时间,Redis 自动释放锁,其他节点获取锁,导致多个节点同时执行业务。

解决方案

  1. 预估合理的锁过期时间,预留足够的业务执行时间(如常规执行 10 秒,设过期 30 秒);
  2. 使用看门狗机制(Redisson 内置),定时为未执行完的业务续约锁的过期时间;
  3. 业务侧优化性能,减少锁持有时间(锁的粒度尽可能小,仅在资源竞争处加锁)。

问题 4:竞态条件

原因:非原子的加锁 / 解锁操作,导致多个节点同时尝试加锁 / 解锁。

解决方案

  1. 加锁使用 Redis 原生原子命令(SET NX EX/PX),禁止分两步执行SETNXEXPIRE
  2. 解锁使用 Lua 脚本实现原子操作;
  3. 高并发场景下,加锁失败后使用自旋重试(短时间重试,避免频繁请求),并设置重试上限。

问题 5:Redis 主从同步延迟导致的锁失效

原因:主节点加锁成功,未将锁同步到从节点,主节点宕机后从节点升主,新主节点无锁,其他节点可重新加锁。

解决方案

  1. 避免在主从 / 哨兵架构下使用单机版 Redis 锁,改用Redlock(基于独立 Redis 节点);
  2. 使用 Redisson 的主从锁(RedissonMasterSlaveLock),适配主从架构,底层通过 Lua 脚本保证主从同步后的锁一致性;
  3. 降低 Redis 主从同步的延迟(如使用主从直连、开启同步确认)。

问题 6:锁的粒度太大

原因:对整个资源加锁(如对所有商品的库存加一把锁),导致并发量急剧下降。

解决方案

  1. 细粒度加锁:按资源维度拆分锁(如按商品 ID 分锁,lock:stock:1001lock:stock:1002);
  2. 分段锁:对大资源分段加锁(如将库存分为 10 段,每段一把锁,扣减库存时仅锁定对应段),提升并发量。

四、Redis 锁的选型建议

Redis 锁的选型核心围绕Redis 部署架构业务并发量功能需求(重入、读写分离)和高可用要求展开,以下是不同场景的最优选型:

场景推荐锁类型实现方式核心优势
Redis 单节点,低并发,无重入需求单机版 Redis 锁原生SET NX EX+Lua 脚本解锁实现简单、性能最高
Redis 单节点 / 主从 / 集群,需要重入 / 公平锁 / 读写锁可重入锁 / 读写锁Redisson 客户端功能丰富、内置看门狗、适配所有架构
Redis 集群,高可用核心业务(如订单、库存)Redlock 红锁Redisson 的 RedLock解决单点故障,锁安全性最高
读多写少的资源竞争场景读写锁Redisson 的 RReadWriteLock读共享写互斥,提升并发量
分布式任务调度,避免任务重复执行单机版 Redis 锁 / Redlock原生命令 + 自旋重试轻量、可靠,支持任务重试

五、Redis 锁与其他分布式锁的对比

分布式锁的实现方案还有ZooKeeper数据库,Redis 锁与它们相比,核心优势是性能,劣势是一致性(Redis 为最终一致性,ZooKeeper 为强一致性),以下是核心对比:

特性Redis 锁ZooKeeper 锁数据库锁
性能极高(内存操作,QPS 可达 10W+)中等(基于 ZAB 协议,磁盘持久化)低(磁盘 IO,行锁 / 表锁性能有限)
实现复杂度中等(原生简单,Redlock/Redisson 复杂)中等(基于临时节点 / 有序节点)简单(基于行锁 / 唯一索引)
高可用高(Redlock / 集群)极高(ZooKeeper 集群,过半可用)中等(数据库主从,需处理锁同步)
死锁风险低(自动过期 + 看门狗)极低(临时节点,会话断开自动删除)高(需手动释放,易出现死锁)
一致性最终一致性强一致性强一致性
适用场景高并发、对一致性要求适中的场景分布式协调、对一致性要求高的场景低并发、小型系统,无需额外组件

六、Redis 锁的最佳实践

  1. 最小锁粒度:仅对资源竞争的核心代码块加锁,避免对整个方法 / 业务流程加锁,减少锁持有时间;
  2. 原子性操作:加锁用SET NX EX/PX,解锁用 Lua 脚本,禁止非原子的分步操作;
  3. 唯一锁标识:必须设置节点 / 线程唯一的锁标识,防止误删其他节点的锁;
  4. 合理过期时间:预估业务执行时间,设置足够的过期时间,高并发场景配合看门狗机制;
  5. 避免自旋过度:加锁失败后,自旋重试需设置重试次数 / 重试间隔,避免频繁请求 Redis 导致性能下降;
  6. 兜底机制:增加异常锁清理任务,定期清理过期未释放的锁,防止极端情况的死锁;
  7. 优先使用成熟框架:避免手动实现 Redlock / 可重入锁,优先使用 Redisson 等成熟客户端,内置各种优化和特性;
  8. 避免锁穿透:加锁前先校验资源是否存在,避免对不存在的资源频繁加锁;
  9. 监控与告警:监控 Redis 锁的加锁成功率、持有时间、过期次数,对异常情况(如加锁失败率过高、锁持有时间过长)及时告警。

总结

Redis 锁是分布式系统中最常用的分布式锁方案,核心围绕原子性过期机制展开,从基础的单机版锁到高可用的 Redlock,再到功能丰富的可重入锁 / 读写锁,满足了不同场景的需求。原生 Redis 仅提供基础的锁能力,实际项目中推荐使用Redisson客户端,其封装了各种 Redis 锁的实现,解决了死锁、误删、看门狗等核心问题,且适配 Redis 所有部署架构。

使用 Redis 锁的核心原则:保证原子性、最小锁粒度、避免死锁、适配业务的并发和一致性要求

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/25 8:13:06

【SSM毕设源码分享】基于ssm+vue的健康医疗体检管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/3/27 11:20:23

防抖与节流在Vue中的实现

防抖与节流在Vue中的实现 在前端开发中&#xff0c;高频事件&#xff08;如输入框输入、窗口缩放、滚动等&#xff09;的频繁触发往往会导致性能问题&#xff0c;如过多的DOM操作、网络请求或计算开销。防抖&#xff08;Debounce&#xff09;和节流&#xff08;Throttle&#x…

作者头像 李华
网站建设 2026/3/27 6:22:58

C++作业调度问题

洛谷&#xff1a;作业调度方案 &#x1f6a9; 作业调度问题&#xff1a;算法笔记 1. 核心模拟策略&#xff1a;见缝插针 这类题目最容易掉入“按时间一分钟一分钟模拟”的陷阱&#xff08;你第一版代码的问题&#xff09;。 正确思路&#xff1a; 按照题目给出的优先顺序&am…

作者头像 李华
网站建设 2026/3/27 16:40:16

一文搞懂大模型 - RAG技术(检索、增强、生成)

RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09; 是一种结合了信息检索技术与语言生成模型的人工智能技术。该技术通过从外部知识库中检索相关信息&#xff0c;并将其作为提示&#xff08;Prompt&#xff09;输入给大型语言模型&#xff0…

作者头像 李华
网站建设 2026/3/20 1:35:30

Redis缓存三大问题深度解析:击穿、穿透与雪崩

引言 Redis作为高性能的内存数据库&#xff0c;在互联网架构中扮演着至关重要的缓存角色。然而在实际应用中&#xff0c;我们常会遇到缓存击穿、穿透和雪崩三大问题&#xff0c;这些问题可能导致系统性能急剧下降&#xff0c;甚至引发服务雪崩。本文将从理论原理、解决方案、代…

作者头像 李华
网站建设 2026/3/20 23:35:22

2026毕设ssm+vue魔方教学网站论文+程序

本系统&#xff08;程序源码&#xff09;带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。 系统程序文件列表 开题报告内容 一、选题背景 关于魔方教学与在线教育资源管理问题的研究&#xff0c;现有研究主要以通用在线教育平台或综合性技能培训系统为主&…

作者头像 李华