命令格式

SETNX key value

将 key 的值设为 value,当且仅当 key 不存在。 
若给定的 key 已经存在,则 SETNX 不做任何动作。 
SETNX 是SET if Not eXists的简写。

返回值

返回整数,具体为 
- 1,当 key 的值被设置 
- 0,当 key 的值没被设置

例子

redis> SETNX mykey “hello” 
(integer) 1 
redis> SETNX mykey “hello” 
(integer) 0 
redis> GET mykey 
“hello” 
redis>

使用SETNX实现分布式锁

多个进程执行以下Redis命令:

SETNX lock.foo <current Unix time + lock timeout + 1>

如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间)。 
如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。

解决死锁

考虑一种情况,如果进程获得锁后,断开了与 Redis 的连接(可能是进程挂掉,或者网络中断),如果没有有效的释放锁的机制,那么其他进程都会处于一直等待的状态,即出现“死锁”。

上面在使用 SETNX 获得锁时,我们将键 lock.foo 的值设置为锁的有效时间,进程获得锁后,其他进程还会不断的检测锁是否已超时,如果超时,那么等待的进程也将有机会获得锁。

然而,锁超时时,我们不能简单地使用 DEL 命令删除键 lock.foo 以释放锁。考虑以下情况,进程P1已经首先获得了锁 lock.foo,然后进程P1挂掉了。进程P2,P3正在不断地检测锁是否已释放或者已超时,执行流程如下:

  • P2和P3进程读取键 lock.foo 的值,检测锁是否已超时(通过比较当前时间和键 lock.foo 的值来判断是否超时)
  • P2和P3进程发现锁 lock.foo 已超时
  • P2执行 DEL lock.foo命令
  • P2执行 SETNX lock.foo命令,并返回1,即P2获得锁
  • P3执行 DEL lock.foo命令将P2刚刚设置的键 lock.foo 删除(这步是由于P3刚才已检测到锁已超时)
  • P3执行 SETNX lock.foo命令,并返回1,即P3获得锁
  • P2和P3同时获得了锁

从上面的情况可以得知,在检测到锁超时后,进程不能直接简单地执行 DEL 删除键的操作以获得锁。

为了解决上述算法可能出现的多个进程同时获得锁的问题,我们再来看以下的算法。 
我们同样假设进程P1已经首先获得了锁 lock.foo,然后进程P1挂掉了。接下来的情况:

  • 进程P4执行 SETNX lock.foo 以尝试获取锁
  • 由于进程P1已获得了锁,所以P4执行 SETNX lock.foo 返回0,即获取锁失败
  • P4执行 GET lock.foo 来检测锁是否已超时,如果没超时,则等待一段时间,再次检测
  • 如果P4检测到锁已超时,即当前的时间大于键 lock.foo 的值,P4会执行以下操作 
    GETSET lock.foo <current Unix timestamp + lock timeout + 1>
  • 由于 GETSET 操作在设置键的值的同时,还会返回键的旧值,通过比较键 lock.foo 的旧值是否小于当前时间,可以判断进程是否已获得锁
  • 假如另一个进程P5也检测到锁已超时,并在P4之前执行了 GETSET 操作,那么P4的 GETSET 操作返回的是一个大于当前时间的时间戳,这样P4就不会获得锁而继续等待。注意到,即使P4接下来将键 lock.foo 的值设置了比P5设置的更大的值也没影响。

另外,值得注意的是,在进程释放锁,即执行 DEL lock.foo 操作前,需要先判断锁是否已超时。如果锁已超时,那么锁可能已由其他进程获得,这时直接执行 DEL lock.foo 操作会导致把其他进程已获得的锁释放掉。

程序代码

public static boolean acquireLock(String lock) {// 1. 通过SETNX试图获取一个lockboolean success = false;Jedis jedis = pool.getResource();       long value = System.currentTimeMillis() + expired + 1;     System.out.println(value);    long acquired = jedis.setnx(lock, String.valueOf(value));//SETNX成功,则成功获取一个锁if (acquired == 1)      success = true;//SETNX失败,说明锁仍然被其他对象保持,检查其是否已经超时else {long oldValue = Long.valueOf(jedis.get(lock));//超时if (oldValue < System.currentTimeMillis()) {String getValue = jedis.getSet(lock, String.valueOf(value));               // 获取锁成功if (Long.valueOf(getValue) == oldValue) success = true;// 已被其他进程捷足先登了else success = false;}//未超时,则直接返回失败else             success = false;}        pool.returnResource(jedis);return success;
}

Redis-SETNX命令简介相关推荐

  1. NoSQL数据库Redis使用命令简介

    NoSQL 数据库Redis使用命令简介 NOSQL数据库可以按照它们的数据模型分成4类: 1.键-值对存储库(Key-Value) redis-- 2.BigTable实现(BigTable-imp ...

  2. Redis Setnx 命令(转)

    转自:Redis Setnx 命令 Redis Setnx 命令 Redis 字符串(string) Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 ...

  3. 使用 Redis的SETNX命令实现分布式锁

    SETNX命令简介 SETNX key value 将key的值设为value,并且仅当key不存在. 若给定的key已经存在,则SETNX不做任何操作. SETNX 是SET if Not eXis ...

  4. php redis list llen,redis llen list 命令简介

    一.命令简介 llen list命令在redis中的主要功能是: 返回一个list(列表)中的元素个数. 二.命令使用说明 命令语法如下: redis 192.168.8.123>llen li ...

  5. Redis分布式锁/Redis的setnx命令如何设置key的失效时间(同时操作setnx和expire)

    Redis的setnx命令是当key不存在时设置key,但setnx不能同时完成expire设置失效时长,不能保证setnx和expire的原子性.我们可以使用set命令完成setnx和expire的 ...

  6. 场景应用:Redis使用setnx命令实现分布式锁

    文章目录 何时需要分布式锁? 如何实现分布式锁? 探究单节点锁:setnx命令 1. 加锁: 2. 解锁: 带来的问题 探究分布式锁:RedLock算法 补充:setnx命令学习 何时需要分布式锁? ...

  7. redis的lrange_redis lrange list命令简介

    一.命令简介 lrange list命令在redis中主要功能是: 根据指定的索引(从开始和结束位置)范围 ,返回索引范围内的元素列表 二.命令使用说明 命令语法如下: redis 192.168.8 ...

  8. redis setnx 原子性_Redis从入门到深入-分布式锁(26)

    1. 分布式锁 1.1 简介 锁 是一种用来解决多个执行线程 访问共享资源 错误或数据不一致问题的工具 如果 把一台服务器比作一个房子,那么 线程就好比里面的住户,当他们想要共同访问一个共享资源,例如 ...

  9. mset redis_redis mset string 命令简介

    一.命令简介 mset string命令在redis中主要功能是: 可以通过一条命令对多个键进行赋值操作, 类似与一次执行多条set命令 二.命令使用说明 命令语法如下: redis 192.168. ...

  10. Redis学习 - NoSQL简介、redis安装、redis基础知识、数据类型、持久化、订阅发布、主从复制、哨兵模式、缓存击穿和雪崩

    学习视频地址:https://www.bilibili.com/video/BV1S54y1R7SB 完结撒花,感谢狂神 文章目录 1. NoSQL 1.1 单机Mysql的演进 1.2 当今企业架构 ...

最新文章

  1. 菜鸟教程:SQL 通用数据类型
  2. oracle 输出 三角形,10. 三角形(示例代码)
  3. 理解Mysql prepare预处理语句:mysql_stmt_init(MYSQL *mysql);
  4. Java Recruitment(1)
  5. CentOS配置主机名和主机映射
  6. 也谈创业企业CEO该拿多少工资
  7. ue4渲染速度太慢_技术汇丨如何在UE4中实现最佳性能和高质量视觉效果
  8. neatupload上传文件配置
  9. 签到系统、评价系统、后台审核系统前后端接口实现总结
  10. 【预测模型】基于蝙蝠算法改进SVM实现预测matlab源码
  11. 一篇文章带你了解国企程序员(超详细)
  12. 光纤是计算机网络介质之一,计算机网络技术传输介质--光纤.pptx
  13. QPSK数字调制系统在AWGN信道下的BER仿真
  14. 大一新生必看,自学必看,里昂详解数据结构之堆栈
  15. Intel Realsense D455 D435i D415 T265 3D实感硬件对比
  16. 礼盒抖动动画(CocosCreator)
  17. 在linux系统中 创建用户账户的同时,在Linux系统中大批量建立帐户
  18. 【R语言】计算信息份额模型 - Computes information share component share weights
  19. 为什么邮件发的邮箱会进垃圾箱?企业邮箱进垃圾箱有什么处理方法?
  20. 机器学习——霍夫丁不等式【转】

热门文章

  1. Autofac的基本使用
  2. 以匠心正道,以决心致远:毫末智行的自动驾驶之路
  3. 机器学习 周志华 第一章课后习题
  4. electron开发问题记录
  5. 简历之精通 熟练 掌握 熟悉 了解
  6. linux怎么查看当前系统版本号,Linux下怎么查看当前系统的版本
  7. DataGrip数据库管理工具安装使用
  8. 运用java爬虫和python做词云图
  9. 二维码如何转为链接,看了这篇文章才知道多好用!
  10. ValueError: greenlet.greenlet size changed, may indicate binary incompatibility. Expected 144 from C