setnx命令

将 key 的值设为 value,当且仅当 key 不存在。

若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是SET if Not eXists的简写。

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

getset命令

将键 key 的值设为 value , 并返回键 key 在被设置之前的旧值。

返回给定键 key 的旧值。

如果键 key 没有旧值, 也即是说, 键 key 在被设置之前并不存在, 那么命令返回 nil

当键 key 存在但不是字符串类型时, 命令返回一个错误。

redis> GETSET db mongodb    # 没有旧值,返回 nil
(nil) redis> GET db
"mongodb" redis> GETSET db redis # 返回旧值 mongodb
"mongodb" redis> GET db
"redis"

分布式锁

多个进程执行以下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 删除键的操作以获得锁。

解决上述问题,在del前,进行 getset操作

我们同样假设进程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 操作会导致把其他进程已获得的锁释放掉。

redis分布式锁不火的原因

  • 按照上面的死锁,redis非常依赖服务器时间,如果时间设置不一致,就会导致问题
  • 现在redis一般为一主多从,如果主机挂了,对上面的 方法也有一系列问题

当然,现在Redis的作者提出了一个更安全的实现,叫做Redlock,但是我们已经习惯使用zk了

参考:

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

基于redis的分布式锁实现

Redis实现分布式锁(setnx、getset、incr)以及如何处理超时情况(一)

Redis setnx命令 分布式缓存的更多相关文章

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

    基于setnx和getset http://blog.csdn.net/lihao21/article/details/49104695 使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其 ...

  2. 使用Redis SETNX 命令实现分布式锁(转载)

    使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其实现方法. SETNX命令简介 命令格式 SETNX key value 将 key 的值设为 value,当且仅当 key 不存在. 若 ...

  3. Redis setNX 实现分布式锁(重复数据插入可用其来实现排他锁)

    使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其实现方法. SETNX命令简介 命令格式 SETNX key value 将 key 的值设为 value,当且仅当 key 不存在. 若 ...

  4. C#客户端Redis服务器的分布式缓存

    介绍 在这篇文章中,我想介绍我知道的一种最紧凑的安装和配置Redis服务器的方式.另外,我想简短地概述一下在.NET / C#客户端下Redis hash(哈希类型)和list(链表)的使用. 在这篇 ...

  5. Redis SETNX实现分布式锁

    1.某进程1执行 SETNX lock 以尝试获取锁 2.由于某进程2已获得了锁,所以进程1执行 SETNX lock 返回0,即获取锁失败 3.进程1执行 GET lock 来检测锁是否已超时,如果 ...

  6. 分布式缓存技术redis学习系列

    分布式缓存技术redis学习系列(一)--redis简介以及linux上的安装以及操作redis问题整理 分布式缓存技术redis学习系列(二)--详细讲解redis数据结构(内存模型)以及常用命令 ...

  7. springboot+mybatis+redis实现分布式缓存

    大家都知道springboot项目都是微服务部署,A服务和B服务分开部署,那么它们如何更新或者获取共有模块的缓存数据,或者给A服务做分布式集群负载,如何确保A服务的所有集群都能同步公共模块的缓存数据, ...

  8. EhCache+Redis实现分布式缓存

    Ehcache集群模式 由于 EhCache 是进程中的缓存系统,一旦将应用部署在集群环境中,每一个节点维护各自的缓存数据,当某个节点对缓存数据进行更新,这些更新的数据无法在其它节点中共享,这不仅会降 ...

  9. Asp.Net Core 轻松学-正确使用分布式缓存

    前言     本来昨天应该更新的,但是由于各种原因,抱歉,让追这个系列的朋友久等了.上一篇文章 在.Net Core 使用缓存和配置依赖策略 讲的是如何使用本地缓存,那么本篇文章就来了解一下如何使用分 ...

随机推荐

  1. CF3A Shortest path of the king

    The king is left alone on the chessboard. In spite of this loneliness, he doesn't lose heart, becaus ...

  2. Background removal with deep learning

    [原文链接] Background removal with deep learning   This post describes our work and research on the gree ...

  3. jquery截取地址栏中url参数的值

    <script> /*http://127.0.0.9/index.php?s=/Home/Index/fangguan_shuju&zc=2*/ function getQuer ...

  4. js中级小知识2

    1.面向对象 js一开始就是写网页特效,面向过程的,作者发现这样写不好,代码重复利用率太高,计算机内存消耗太大,网页性能很差.所以作者就收到java和c的影响,往面向对象靠齐.js天生有一个Objec ...

  5. JavaScript基础知识(三个判断、三个循环)

    三个判断 if…else…只会执行其中一个条件 如果if条件中只有一个值,那么会默认转布尔: if(1=="1"){ // 当括号中条件为true时,执行此处的代码 console ...

  6. Vijos 1360 - 八数码问题 - [A*]

    题目链接:https://vijos.org/p/1360 优先队列BFS: 这个八数码问题本身其实是之前人工智能实验课的作业…… 首先,如果不带估价函数,直接用优先队列BFS,肯定也是能得到正确结果 ...

  7. Open_stack 有虚拟机端口不通的问题

    原因:流量表 解决办法:not修改流量表(容易出错,出错后果更严重):is迁移虚拟机这样会对应生成新的流量表,然后问题就解决了.

  8. PHP日常模拟业务的小工具

    随机生成姓名 public function getChar($num=2) // $num为生成汉字的数量 { $first = array('赵','钱','孙','李','周','吴','郑', ...

  9. kafka8 编写简单消费者

    1.eclipse运行消费者代码.代码如下 package cn.test.mykafka; import java.util.Arrays; import java.util.Properties; ...

  10. javascript常用的操作

    1.concat()  连接两个或更多的数组,并返回一个新的数组.注意:该方法不会改变原数组 var arry1=["李四",“王二”]:  var arry2=['赵柳',“李旺 ...