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

使用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.dtcms.com/a/319318.html

相关文章:

  • python的web接口数据库链接封装
  • Linux---第三天---权限
  • 并发测试:你的应用扛得住“早高峰”吗?
  • python中的集合
  • uniapp vue3中使用pinia 和 pinia持久化(没有使用ts)
  • PPT漏斗图,让数据更美观!
  • 线程池创建线程
  • Mac 电脑安装 ADB 环境完整指南
  • 流程图使用规范
  • Git Status 命令深度指南:洞悉仓库状态的核心艺术
  • 专题二_滑动窗口_长度最小的子数组
  • 进程Linux
  • 打靶日记-PHPinclude-labs(一)
  • 攻防世界WEB(新手模式)2-2-upload1
  • MySQL中的DDL(一)
  • 深入理解动态规划算法
  • 数字孪生系统让汽车工厂虚实联动预测维护少停机
  • LabVIEW注册表操作
  • 图像认知与OpenCV——图像预处理4
  • python opencv 调用 海康威视工业相机(又全又细又简洁)
  • Java开发时出现的问题---架构与工程实践缺陷
  • KUKA库卡焊接机器人氩气节气设备
  • Nuclei漏洞扫描工具(除了常见漏洞还支持CMS常见漏洞Gitlab、Jira、Splunk、Elastic)
  • 解决Git提交人信息默认全局化问题:让提交人自动关联当前用户
  • sklearn study notes[3]
  • Kong vs. NGINX:从反向代理到云原生网关的全景对比
  • 从零开始学习:深度学习(基础入门版)(1天)
  • [激光原理与应用-168]:测量仪器 - 对光学指标进行测量的仪器
  • 从“数字网格”到“空中交警” :星图低空云如何重构低空管理?
  • 嵌入式第二十二课!!!链式队列与哈希表