Redis(十四)——分布式锁

锁其实我们已经有了一定的了解了,尤其是对于数据库锁来说,但是redis锁他有什么作用呢?我们都知道redis是一个读写单线程的,那加锁还有一样吗?有的,当然是有的,和数据库的锁相比,redis的锁其实是一种业务锁,他锁的是这个业务,而数据库的锁,他锁的是这个数据,这个就是他们最大的区别。

而我们的java本身也是有锁的,但是并不是分布式的,因为java的锁只能管当前JVM中的线程,然后是分布式服务那么java的锁就没有用了,得需要借助其他的中间件。

分布式锁的特性

独立性

这个很好理解,就是同一时刻只有一个线程持有该锁。总不能A和B都有各干各的不是乱套了

高可用

首先就是在高并发的情况下,系统的性能可以得到保障,不能因为加锁也导致系统阻塞,服务不可用吧,其次就是在redis集群的情况下,然后一个节点挂了,不能因为挂了个节点导致获取锁或者释放锁失败

防死锁

必须有兜底机制,不能因为一个业务卡住导致一直持有锁,一定要设置超时机制或者撤销机制

不乱抢

自己只能获取自己的锁,释放自己的锁,不能把别人的锁给抢了或者释放了,这个是怎么来的呢,就是一个A业务卡住啦,超时释放啦锁,刚好这个时候B获取啦这个锁,等到A回过头来,把B的锁给释放啦,那B不就懵逼啦。

重入性

是指同一个线程(或进程)可以多次获取同一把锁而不会导致死锁。也就是我们的递归或者嵌套方法,如果不能同一把锁的话,就会导致死锁,因为一直没被释放,而且这个方法一定会死锁。

setnx

这个我们应该很熟悉啦,就是只有存在才能设置,最简单的分布式锁就是使用这个命令,获取锁其实就是set锁,等到key过期或者等到线程释放那么就是删除啦这个key,其他线程才能再次set再次获取,很好理解吧,那为什么会是分布式的呢?其实也很好理解,因为不管是主从、哨兵、集群都是多个从机,而且set只能是主机,所以就是分布式的啦。

但是redis2的时候还不能一条命令直接设置,需要使用setnx设置成功后,要给这个key设置过期时间,这个设置时间的过程可能就有其他命令篡改了,所以之前有setnx+lua脚本组合实现分布式锁的方式,因为lua脚本是原子的,但是lua脚本主包没学过,所以有兴趣的小伙伴自行修炼吧。

复制代码
# ❌ SETNX (SET if Not eXists) - 2.6.12之前
SETNX key value
EXPIRE key seconds
# 需要两条命令,不是原子操作

# ✅ SET 带NX参数 (SET with NX) - 2.6.12及之后
SET key value NX EX seconds
# 一条命令,原子操作

问题

问题/缺点 SETNX+EXPIRE方案 SET with NX EX方案 严重程度 影响
原子性问题 ⚠️ 两条命令非原子 ✅ 单条命令原子 严重 可能导致死锁
死锁风险 ⚠️ 高(崩溃导致锁不过期) ✅ 低 严重 系统不可用
时钟漂移 ⚠️ 受系统时钟影响 ⚠️ 受系统时钟影响 锁提前/延迟释放
网络延迟 ⚠️ 可能过期时间不准确 ⚠️ 可能过期时间不准确 锁时间不准确
可重入性 ❌ 不支持 ❌ 不支持 复杂业务受限
自动续期 ❌ 不支持 ❌ 不支持 长任务风险
锁误删 ⚠️ 可能(需配合Lua) ⚠️ 可能(需配合Lua) 数据不一致
集群问题 ⚠️ 主从同步延迟 ⚠️ 主从同步延迟 锁失效
性能影响 ⚠️ 两次网络往返 ✅ 一次网络往返 延迟增加
实现复杂度 ⚠️ 较高 ⚠️ 中等 维护成本

我们一个一个来说哈,首先是这个原子性问题,刚刚我们已经说过了,然后就是这个死锁,如果setnx成功后服务器挂了,不是redis哦,那么这个key就永远不会过期,就死锁了。

然后就是时钟和网络延迟,时钟就是服务器的时间可能和redis的时间有点不同,那么过期的时间就会有冲突,网络延迟就是从机还没同步好,导致其他线程误以为没有尝试获取锁。

可重入性这个上面也有说到,其实setnx和set也可以实现这个,就是value使用分布式机器id,这样使用递归的时候,同一个机器的肯定是可以获取到锁的。

自动续期就是一些业务设置的过期时间是30秒,但是由于GC或者其他原因导致60秒才能完成,这样的可能就会出现吴删除,就是把其他线程的key给删除了,因为执行完肯定是要删除key的嘛,可以使用看门狗机制进行续期,就是只要任务没成功,看门狗这个任务没释放,那么定期就给key续命。

误删除刚刚已经说了,只是结合lua会更加安全,也就是删除前先检查是不是直接的key再删除,但是也可能存在误删,比如同一个机器的同一个业务,但是也有解决办法,比如利用系统时间加一个盐得到一个数,把他和分布式机器id结合,那么误删除率就大大减少了。

Redisson

下面这个是简单的对比表格,可以抗到redisson要比普通的setnx强大太多了,这个也是无可厚非的,因为redisson怎么说也是一个框架或者叫做一个中间件,和消息队列一样专业的事情交给专业的人来干。

Redisson方案 SETNX方案
成熟度 ✅ 生产级框架 ⚠️ 基础实现
易用性 ✅ 高(API简单) ❌ 低(需手写)
安全性 ✅ 高(防各种异常) ⚠️ 中(需完善)
性能 ✅ 高(优化好) ✅ 高(原始操作)
功能完整性 ✅ 丰富功能 ❌ 基础功能
维护成本 ✅ 低(社区维护) ⚠️ 高(自维护)
学习成本 ⚠️ 中(需学框架) ✅ 低(简单)

这个玩意怎么用这里就不说啦,是一个非常强大的框架,解决的就是redis分布式锁的关系,通过lua脚本和看门狗机制保障不会出现setnx出现的问题。看门狗机制给key续命,之前我们讲缓存双写的时候也说过看门狗机制,简单来说,看门狗机制就是一种保障机制,开启业务后另外再开一个线程,只要主业务没报错,就会定期的去redis中给key续命,通常为过期时间的二分之一,直到业务结束,使用lua脚本释放锁,然后再释放这个看门狗线程。如果主业务报错啦,看门狗机制就会执行预定的错误逻辑,比如重试或者发送日志等操作。

这里再简单讲一下redisson的流程,他其实有2个时间,一个是存活时间,一个是等待时间,如果在等待时间内抢到啦锁,就执行上门说的看门狗流程,如果没抢到就放回错误。

RedLock红锁

RedLock ​ 是Redis作者Antirez 提出的一个分布式锁算法,用于在多个独立Redis实例上实现高可用分布式锁。我们上面说的redisson也是支持红锁的,但是现在却不建议使用了,因为他还是会出现超卖的问题。

其实上面讲的不管是setnx还是redisson还是现在的redlock都会存在超卖的问题,只是出现概率的多少的问题,因为我们这个是分布式锁,而且写入值只能在主机,那么主从复制的时候因为是异步的就一定会有超卖的问题。

而redlock就是要求向所以节点请求加锁,只有达到n/2+1个节点都加锁成功啦,才算获取到锁了,如果没有或者超过了锁过期时间到一半,那么就认为获取锁失败了,锁会自动过期。看起来好像解决了分布式锁不一致的情况,其实并没有完全解决,如果一个节点加锁成功了,但是还没有同步到从机就挂了,那么从机上位就不会有这个锁,那么其他线程请求这个锁就会被成功,那么还是会出现误删除或者锁竞争的问题。

另外还有时钟不同步问题​​:RedLock 依赖系统时间计算锁过期时间。若节点间时钟漂移(如某节点时间快),可能导致锁提前失效,其他客户端可重复获取锁,破坏互斥性。

​GC停顿导致锁失效​​:客户端A在部分节点加锁后发生GC停顿(如JVM STW),锁因超时自动释放。客户端B成功获取锁并操作资源,而客户端A恢复后误以为仍持有锁,导致数据并发冲突。

​网络延迟敏感​​:需等待多数节点(N/2+1)响应才能加锁成功,高延迟环境下性能显著下降。

​维护成本高​​:需部署多个独立Redis主节点(通常≥5个),且需保证节点无主从复制关系,增加运维复杂度。

结论

其实红锁还是个不错的解决方案,但是只针对于允许部分出错的业务场景,比如推送消息之类的简单业务场景,redisson推荐的替代方案是:

Redisson 普通锁(RLock)

他其实和红锁很相似,区别就是加锁后通过WAIT命令等待异步复制到从节点(需Redis 3.0+),降低主从切换导致锁失效的概率。看门狗机制还是有的,只是为了获得更低的容错率,获取锁的时间就变长了,没办法这个因为需要等待异步复制。

ZooKeeper的分布式锁​

如果是对业务有强一致的需求,那么使用专业的框架ZooKeeper 基于临时有序节点和Watcher机制,确保锁互斥性与释放可靠性。客户端会话结束(如宕机)时,临时节点自动删除,避免死锁。金融交易、库存扣减等高一致性要求的场景。如使用Apache Curator的InterProcessMutex。这里介绍一个很好的文章,大家可以去看看https://bbs.huaweicloud.com/blogs/459097

总结

本篇主要说了分布式锁。

相关推荐
镇潮2 小时前
Cursor 接入 mysql mcp
数据库·mysql·ai
老华带你飞2 小时前
宠物商城销售|基于Java+ vue宠物商城销售管理系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·宠物
不想画图2 小时前
redis高可用-主从复制和哨兵模式
数据库·redis
九章-2 小时前
自主可控:三峡新能源打造全栈国产化光伏监控系统新标杆
数据库·安全·能源
武子康2 小时前
Java-190 EVCache入门:Netflix 级分布式缓存架构、性能指标与多区域部署全解析
java·redis·分布式·缓存·架构·guava·guava cache
yeshihouhou2 小时前
redis(hash)使用场景
redis·python·哈希算法
l1t2 小时前
利用Duckdb求解Advent of Code 2025第9题 最大矩形面积
数据库·sql·算法·duckdb·advent of code
染指11102 小时前
70.渗透-Mysql基础-创建数据库
数据库·mysql
LFly_ice3 小时前
Nest-管道
android·java·数据库