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. codeforces-707 C. Pythagorean Triples

    C. Pythagorean Triples time limit per test 1 second memory limit per test 256 megabytes input standa ...

  2. Web前端攻击方式及防御措施

    一.XSS [Cross Site Script]跨站脚本攻击 恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户 ...

  3. Broadcast

    静态注册广播接收器 1. 活动中创建内部类继承BroadcastReceiver实现 onReceive函数 2. new 一个内部类的对象 3. registerReceiver注册内部类 4. 在 ...

  4. python--列表,元组,字符串互相转换

    列表,元组和字符串python中有三个内建函数:,他们之间的互相转换使用三个函数,str(),tuple()和list(),具体示例如下所示 >>> s = "xxxxx& ...

  5. pytorch入门与实践-2.2

    Tensor 1--本质上可以理解为具有不同维度的数组 2--支持的基本运算 |---创建Tensor:  x=t.tensor(x,y) x,y 表示数组的大小 , x=t.rand(x,y), x ...

  6. 关闭shift中英文切换 英文代码/中文注释随意切换着写。

    x 背景 写代码的时候总是意外的就切成中文了,特别是代码中大小写切换的这种情况... 例如:"public static TimeZone CurrentTime..."publi ...

  7. [摘录] 图灵机与lambda演算的关系

    在阅读函数式编程相关资料时,看到如下一段话.感觉说的很好,可以帮助我这种学渣一点点的建立起整个知识体系. 以下片段,摘抄自豆瓣网友 赛义甫 的豆列 “逻辑与计算” 中的一段介绍. 莱布尼兹曾经有两个梦 ...

  8. git的简单玩法

    本篇笔记参考廖雪峰的git教程,为方便查看将命令部分提取并记录下来. 无意对原作的版权侵犯,如需要学习请到廖雪峰网站学习git 创建git仓库 # mkdir learngit && ...

  9. 小型网站使用高德地图开发定位模块需要的php代码

    项目要求: 1.使用定位,获取gps信息 2.获取当前所在城市 3.从该城市中取数据,按照距离我的当前位置远近排序 方案 1.使用js获取当前位置信息,然后使用cookie或者session存储 fu ...

  10. Date Calendar

    1 毫秒值概念 时间和日期的计算,必须依赖毫秒值获取当前日期的毫秒值:System.currentTimeMillis() 返回值long类型参数, 时间原点:公元1970年1月1日,午夜0:00:0 ...