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

总结

本篇主要说了分布式锁。

相关推荐
科技小花34 分钟前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸35 分钟前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain37 分钟前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希1 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神1 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员1 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java2 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿2 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴2 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存
YOU OU2 小时前
三大范式和E-R图
数据库