核心内容摘要
obs-multi-rtmp:多平台协同直播的效率工具
Redis 中的锁核心实现、类型与最佳实践文章目录Redis 中的锁核心实现、类型与最佳实践
Redis 锁的核心基础原子性与过期机制核心基础命令
Redis 锁的主要类型类型 1单机版 Redis 锁基础版适用场景核心实现逻辑核心代码伪代码优缺点类型 2Redis 分布式锁Redlock 红锁Redis 官方推荐适用场景核心设计思想核心实现步骤以 5 个独立 Redis 节点为例优缺点
注意事项类型 3Redis 可重入锁基于 Redisson 实现适用场景核心实现以 Redisson 为例核心代码Redisson 可重入锁核心优势类型 4Redis 读写锁RReadWriteLock适用场景核心规则基于 Redisson核心代码Redisson 读写锁
Redis 锁的
常见问题与解决方案问题 1死锁问题 2锁的误删问题 3锁提前释放问题 4竞态条件问题 5Redis 主从同步延迟导致的锁失效问题 6锁的粒度太大
Redis 锁的选型建议
Redis 锁与其他分布式锁的对比
Redis 锁的最佳实践
总结Redis 作为高性能的内存键值数据库凭借单线程原子性、高读写速度和丰富的命令集成为分布式系统中实现分布式锁的主流方案。
Redis 锁主要解决分布式环境下的资源竞争问题保证多个服务 / 节点对同一资源的操作互斥性核心分为单机版 Redis 锁和分布式 Redis 锁适配 Redis 集群 / 主从架构同时衍生出多种优化实现。
Redis 锁的核心基础原子性与过期机制Redis 实现锁的核心依赖两个特性也是区别于普通内存锁的关键命令原子性Redis 单线程执行命令且提供SETNX、SET带多参数等原子命令避免锁的 “加锁 - 判空” 操作出现竞态条件比如 A 线程判空后B 线程同时完成加锁。
过期自动释放为锁设置过期时间避免持有锁的节点因宕机、网络异常等原因无法手动释放锁导致死锁。
核心基础命令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加锁与设过期非原子操作易导致死锁SETNX和EXPIRE是两个独立的 Redis 命令无法保证原子性。
单独用SETNX加锁会出现极端的异常场景线程 A 执行SETNX成功加锁但还没来得及执行EXPIRE设过期就因为机器宕机、网络中断、进程被杀等原因终止此时lock_key被创建但没有过期时间成为 “永久锁”其他所有线程都无法通过SETNX获取该锁导致死锁资源永远无法被释放。
这种场景在分布式系统中无法避免比如节点重启、网络抖动一旦发生就会导致业务阻塞是生产环境的重大隐患。
Redis 锁的主要类型根据 Redis 的部署架构和实现复杂度Redis 锁分为单机版、分布式版和官方标准化版适配从单节点到高可用集群的不同场景安全性和实现复杂度依次提升。
类型 1单机版 Redis 锁基础版适用场景Redis 单节点部署无主从 / 集群适用于低并发、对高可用要求不高的场景如小型单体应用的分布式部署。
核心实现逻辑加锁使用SET lock_key 唯一标识 NX EX 3030 为过期时间单位秒唯一标识如 UUID 节点 ID用于验证锁的持有者避免误删。
解锁先校验锁的 value 是否为当前节点的唯一标识再删除 key必须通过 Lua 脚本实现原子性否则校验和删除分两步会出现误删。
重试加锁失败时可通过自旋循环重试或阻塞等待实现重入。
核心代码伪代码// 加锁StringlockKeyresource:lock;StringlockValueUUID.randomUUID().toString()-nodeId;// NX仅不存在时加锁PX 30000过期30秒原子操作BooleanlockSuccessredisTemplate.opsForValue().setIfAbsent(lockKey,lockValue,30,TimeUnit.SECONDS);if(!lockSuccess){return加锁失败资源被占用;}// 业务逻辑try{doBusiness();}finally{// 解锁Lua脚本保证原子校验删除StringluaScriptif 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 单节点宕机后整个锁服务不可用不支持重入需额外扩展。
类型 2Redis 分布式锁Redlock 红锁Redis 官方推荐适用场景Redis 主从 / 哨兵 / 集群部署对锁的高可用要求高如电商库存扣减、订单创建等核心业务解决单机版锁的 “单点故障” 问题。
核心设计思想由 Redis 作者 Antirez 提出基于多个独立的 Redis 主节点无主从关系通常 3/5 个实现分布式锁要求在超过半数的节点上成功加锁才认为整体加锁成功解锁时需删除所有节点的锁。
核心实现步骤以 5 个独立 Redis 节点为例生成唯一标识同单机版用于标识锁持有者逐个加锁依次向 5 个 Redis 节点发送SET lock_key 唯一标识 NX EX 超时时间命令设置统一的加锁超时时间远小于锁的过期时间避免某个节点宕机导致阻塞判断加锁成功若超过半数节点≥3 个加锁成功且总耗时≤锁的过期时间则加锁成功否则立即向所有节点发起解锁加锁失败执行业务加锁成功后执行互斥业务统一解锁向所有 5 个节点发送解锁命令Lua 脚本无论节点是否加锁成功。
优缺点优点解决 Redis 单点故障高可用锁的安全性远高于单机版缺点实现复杂需维护多个独立 Redis 节点性能比单机版低需逐个节点加锁存在时钟漂移风险节点间时间不同步可能导致锁提前过期。
注意事项红锁要求 Redis 节点完全独立不能是主从 / 哨兵架构主从同步存在延迟主节点宕机后从节点升主可能出现多个节点持有锁加锁超时时间必须远小于锁的过期时间建议为锁过期时间的 1/5~1/3避免网络延迟导致总耗时过长。
类型 3Redis 可重入锁基于 Redisson 实现适用场景需要锁重入的业务如方法 A 加锁后调用的方法 B 也需要对同一资源加锁无需重复加锁是对基础 Redis 锁的功能扩展原生 Redis 不支持重入需通过第三方框架实现。
核心实现以 Redisson 为例Redisson 是 Redis 的 Java 客户端基于 Redis 实现了可重入锁、公平锁、读写锁等多种分布式锁底层通过Lua 脚本和Hash 结构实现重入加锁时用 Hash 结构存储锁key - {持有者标识: 重入次数}首次加锁创建 Hash重入次数设为 1同时设置过期时间重入加锁校验持有者标识为当前节点原子性将重入次数 1解锁校验持有者标识重入次数 - 1若次数为 0 则删除 Hash释放锁否则仅更新次数看门狗机制若业务未执行完成锁的过期时间将到期Redisson 会通过定时任务自动续约锁的过期时间默认每 10 秒续约锁默认过期 30 秒避免锁提前释放。
核心代码Redisson 可重入锁// 初始化Redisson客户端ConfigconfignewConfig();config.useSingleServer().setAddress(redis://
127.
0.
1:
;RedissonClientredissonRedisson.create(config);// 获取可重入锁RLocklockredisson.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 单机、主从、集群、哨兵等所有部署架构提供阻塞、非阻塞、自旋等多种加锁方式。
类型 4Redis 读写锁RReadWriteLock适用场景读多写少的资源竞争场景如商品详情页缓存、文章内容查询实现读共享、写互斥提升并发量若用普通互斥锁读操作也会互斥降低性能。
核心规则基于 Redisson读锁多个节点可同时获取读锁无互斥若已有写锁则读锁获取失败写锁排他锁仅一个节点可获取若已有读锁 / 写锁则写锁获取失败锁升级不支持读锁不能直接升级为写锁需先释放读锁锁降级支持写锁可降级为读锁无需释放写锁直接获取读锁。
核心代码Redisson 读写锁RReadWriteLockrwLockredisson.getReadWriteLock(resource:rwlock);// 获取读锁RLockreadLockrwLock.readLock();// 获取写锁RLockwriteLockrwLock.writeLock();// 写操作排他锁writeLock.lock(30,TimeUnit.SECONDS);try{// 更新资源如修改商品库存updateResource();}finally{writeLock.unlock();}// 读操作共享锁readLock.lock(30,TimeUnit.SECONDS);try{// 查询资源如查询商品库存getResource();}finally{readLock.unlock();}
Redis 锁的
常见问题与解决方案Redis 锁的实现中若忽略细节会出现死锁、误删、锁提前释放、并发安全等问题以下是核心问题及通用解决方案问题 1死锁原因持有锁的节点宕机 / 网络异常无法手动释放锁且锁未设置过期时间。
解决方案加锁时必须原子性设置过期时间SET NX EX/PX即使节点宕机Redis 也会自动释放锁高可用场景下使用 Redlock避免单节点宕机导致锁无法释放业务侧增加兜底任务清理过期未释放的异常锁。
问题 2锁的误删原因节点 A 的锁因过期自动释放节点 B 获取锁此时节点 A 执行完业务直接DEL锁误删节点 B 的锁。
解决方案加锁时设置唯一的锁标识如 UUID 节点 ID / 线程 ID解锁前先校验标识是否为当前节点所有解锁操作必须原子性通过 Lua 脚本实现 “校验 删除”禁止分两步执行GET和DEL。
问题 3锁提前释放原因业务执行时间超过锁的过期时间Redis 自动释放锁其他节点获取锁导致多个节点同时执行业务。
解决方案预估合理的锁过期时间预留足够的业务执行时间如常规执行 10 秒设过期 30 秒使用看门狗机制Redisson 内置定时为未执行完的业务续约锁的过期时间业务侧优化性能减少锁持有时间锁的粒度尽可能小仅在资源竞争处加锁。
问题 4竞态条件原因非原子的加锁 / 解锁操作导致多个节点同时尝试加锁 / 解锁。
解决方案加锁使用 Redis 原生原子命令SET NX EX/PX禁止分两步执行SETNX和EXPIRE解锁使用 Lua 脚本实现原子操作高并发场景下加锁失败后使用自旋重试短时间重试避免频繁请求并设置重试上限。
问题 5Redis 主从同步延迟导致的锁失效原因主节点加锁成功未将锁同步到从节点主节点宕机后从节点升主新主节点无锁其他节点可重新加锁。
解决方案避免在主从 / 哨兵架构下使用单机版 Redis 锁改用Redlock基于独立 Redis 节点使用 Redisson 的主从锁RedissonMasterSlaveLock适配主从架构底层通过 Lua 脚本保证主从同步后的锁一致性降低 Redis 主从同步的延迟如使用主从直连、开启同步确认。
问题 6锁的粒度太大原因对整个资源加锁如对所有商品的库存加一把锁导致并发量急剧下降。
解决方案细粒度加锁按资源维度拆分锁如按商品 ID 分锁lock:stock:
lock:stock:1002分段锁对大资源分段加锁如将库存分为 10 段每段一把锁扣减库存时仅锁定对应段提升并发量。
Redis 锁的选型建议Redis 锁的选型核心围绕Redis 部署架构、业务并发量、功能需求重入、读写分离和高可用要求展开以下是不同场景的最优选型场景推荐锁类型实现方式核心优势Redis 单节点低并发无重入需求单机版 Redis 锁原生SET NX EXLua 脚本解锁实现简单、性能最高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 锁的最佳实践最小锁粒度仅对资源竞争的核心代码块加锁避免对整个方法 / 业务流程加锁减少锁持有时间原子性操作加锁用SET NX EX/PX解锁用 Lua 脚本禁止非原子的分步操作唯一锁标识必须设置节点 / 线程唯一的锁标识防止误删其他节点的锁合理过期时间预估业务执行时间设置足够的过期时间高并发场景配合看门狗机制避免自旋过度加锁失败后自旋重试需设置重试次数 / 重试间隔避免频繁请求 Redis 导致性能下降兜底机制增加异常锁清理任务定期清理过期未释放的锁防止极端情况的死锁优先使用成熟框架避免手动实现 Redlock / 可重入锁优先使用 Redisson 等成熟客户端内置各种优化和特性避免锁穿透加锁前先校验资源是否存在避免对不存在的资源频繁加锁监控与告警监控 Redis 锁的加锁成功率、持有时间、过期次数对异常情况如加锁失败率过高、锁持有时间过长及时告警。
总结Redis 锁是分布式系统中最常用的分布式锁方案核心围绕原子性和过期机制展开从基础的单机版锁到高可用的 Redlock再到功能丰富的可重入锁 / 读写锁满足了不同场景的需求。
原生 Redis 仅提供基础的锁能力实际项目中推荐使用Redisson客户端其封装了各种 Redis 锁的实现解决了死锁、误删、看门狗等核心问题且适配 Redis 所有部署架构。
使用 Redis 锁的核心原则保证原子性、最小锁粒度、避免死锁、适配业务的并发和一致性要求。