当前位置: 首页 > ai >正文

使用SETNX实现分布式锁

Redis 中,Setnx(SET if Not eXists)命令是指:只有当指定的 key 不存在时,才会为 key 设置指定的值,此时设置成功,返回 1;如果指定 key 存在,不会覆盖该 key 的值,此时设置失败,返回 0。

因此,利用 Redis 执行命令时是单线程的特性 + Setnx 并发操作的原子性可以实现一个简单的分布式锁。

Redis 执行命令时是单线程的特性可以保证:当多个客户端同时通过 Setnx 命令尝试获取锁时,只有一个客户端会获取成功,其他客户端则获取失败。

Setnx 并发操作的原子性可以保证:多个客户端并发操作的互斥性,即当一个客户端执行 Setnx 命令时,不会被其他客户端影响。

此外,还要考虑:如果获取锁成功,则设置一个过期时间,防止该客户端挂了之后一直持有该锁;客户端释放锁的时候,需要先判断该锁是否仍然属于该客户端,如果是,则通过 DEL 命令释放锁。

实现代码:

public class RedisDistributedLock {private final JedisPool jedisPool;public RedisDistributedLock(JedisPool jedisPool) {this.jedisPool = jedisPool;}public boolean tryLock(String lockKey, String requestId, int expireTime) {try (Jedis jedis = jedisPool.getResource()) {String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);return "OK".equals(result);}}public boolean unlock(String lockKey, String requestId) {try (Jedis jedis = jedisPool.getResource()) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));return Long.parseLong(result.toString()) == 1L;}}}

1、tryLock方法接收三个参数,分别是锁的键值lockKey、加锁的请求标识requestId和锁的过期时间expireTime。该方法会尝试使用Redis的set命令加锁,如果加锁成功则返回true,否则返回false。其中NX表示只在锁的键不存在时设置锁,PX表示锁的过期时间为expireTime毫秒。

2、unlock方法接收两个参数,分别是锁的键值lockKey和加锁的请求标识requestId。该方法会执行一个Lua脚本,判断当前锁的值是否等于请求标识requestId,如果是则删除锁并返回true,否则返回false。该方法使用eval命令执行Lua脚本,传入锁的键值和请求标识两个参数,返回值是执行结果。

优点:

(1)实现简单:SETNX 命令实现简单,易于理解和使用。

(2)性能较高:由于 SETNX 命令的执行原子性,保证了分布式锁的正确性,而且在 Redis 中,SETNX 命令是单线程执行的,所以性能较高。

缺点:

(1)锁无法续期:如果加锁方在加锁后的执行时间较长,而锁的超时时间设置的较短,可能导致锁被误释放。

(2)无法避免死锁:如果加锁方在加锁后未能及时解锁(也未设置超时时间),且该客户端崩溃,可能导致死锁。

(3)存在竞争:由于 SETNX 命令是对 Key 的操作,所以在高并发情况下,多个客户端之间仍可能存在竞争,从而影响性能。

(4)SETNX 不支持可重入,可以借助 Redission 封装的能力实现可重入锁。

总结:

使用 SETNX 命令是 Redis 实现分布式锁最简单的方法,虽然上述方案中并不支持可重入性,但是依然可以在当前逻辑的基础上进行调整,使其支持可重入。 尽管如此,使用 SETNX 命令仍然有其他问题,比如锁无法续期等问题。

其次,Redission 中已经实现了效果不错的分布式锁,开箱即用即可,这里我们只是思考一下基于 SETNX 实现分布式锁的思路而已。

http://www.xdnf.cn/news/17199.html

相关文章:

  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘spacy’问题
  • 【C#补全计划:类和对象(九)】接口
  • 嵌入式开发硬件——单片机
  • QtC++ 中使用 qtwebsocket 开源库实现基于websocket的本地服务开发详解
  • Java中接口与抽象类
  • 【MATLAB】(十)符号运算
  • idea开发工具中git如何忽略编译文件build、gradle的文件?
  • 为什么 `source ~/.bashrc` 在 systemd 或 crontab 中不生效
  • 安卓开发:网络状态监听封装的奥秘
  • vLLM:彻底改变大型语言模型推理延迟和吞吐量
  • 【Apache Olingo】全面深入分析报告-OData
  • count(0),count(*),count(1),count(列)有什么区别?
  • Caffeine 三种过期策略详解
  • java - 深拷贝 浅拷贝
  • 大模型2位量化原理解析
  • 【线性代数】5特征值和特征向量
  • “认知裂缝边缘”地带
  • 共识算法介绍
  • DrissionPage自动化:高效Web操作新选择
  • uniapp-vue2导航栏全局自动下拉变色
  • 360纳米AI、实在Agent、CrewAI与AutoGen……浅析多智能体协作系统
  • 下载 | Windows Server 2016最新原版ISO映像!(集成7月更新、标准版、数据中心版、14393.8246)
  • 智能制造的中枢神经工控机在自动化产线中的关键角色
  • 恒虚警检测(CFAR)仿真:杂波边缘与多目标场景分析
  • 代码随想录算法训练营 Day20
  • Oracle 19C In-Memory 列存储技术测试
  • Numpy科学计算与数据分析:Numpy数组创建与应用入门
  • TypeScript 中高频出现的类型结构与用法
  • C++模板知识点6『拆分模板参数』
  • 任务进度状态同步 万能版 参考 工厂+策略+观察者设计模式 +锁设计 springboot+redission