Redis命令:SETNX key value(SET if Not eXists)
起始版本:1.0.0
时间复杂度:O(1)
将key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。
返回值
Integer reply, 特定值:
1 如果key被设置了
0 如果key没有被设置
例子
redis> SETNX mykey "Hello"
(integer) 1
redis> SETNX mykey "World"
(integer) 0
redis> GET mykey
"Hello"
redis>
Design pattern: Locking with !SETNX
设计模式:使用!SETNX加锁
Please note that:
请注意:
不鼓励以下模式来实现the Redlock algorithm ,该算法实现起来有一些复杂,但是提供了更好的保证并且具有容错性。
无论如何,我们保留旧的模式,因为肯定存在一些已实现的方法链接到该页面作为引用。而且,这是一个有趣的例子说明Redis命令能够被用来作为编程原语的。
无论如何,即使假设一个单例的加锁原语,但是从 2.6.12 开始,可以创建一个更加简单的加锁原语,相当于使用SET命令来获取锁,并且用一个简单的 Lua 脚本来释放锁。该模式被记录在SET命令的页面中。
也就是说,SETNX能够被使用并且以前也在被使用去作为一个加锁原语。例如,获取键为foo的锁,客户端可以尝试一下操作:
SETNX lock.foo <current Unix time + lock timeout + 1>
如果客户端获得锁,SETNX返回1,那么将lock.foo键的Unix时间设置为不在被认为有效的时间。客户端随后会使用DEL lock.foo去释放该锁。
如果SETNX返回0,那么该键已经被其他的客户端锁定。如果这是一个非阻塞的锁,才能立刻返回给调用者,或者尝试重新获取该锁,直到成功或者过期超时。
处理死锁
以上加锁算法存在一个问题:如果客户端出现故障,崩溃或者其他情况无法释放该锁会发生什么情况?这是能够检测到这种情况,因为该锁包含一个Unix时间戳,如果这样一个时间戳等于当前的Unix时间,该锁将不再有效。
当以下这种情况发生时,我们不能调用DEL来删除该锁,并且尝试执行一个SETNX,因为这里存在一个竞态条件,当多个客户端察觉到一个过期的锁并且都尝试去释放它。
C1 和 C2 读lock.foo检查时间戳,因为他们执行完SETNX后都被返回了0,因为锁仍然被 C3 所持有,并且 C3 已经崩溃。
C1 发送DEL lock.foo
C1 发送SETNX lock.foo命令并且成功返回
C2 发送DEL lock.foo
C2 发送SETNX lock.foo命令并且成功返回
错误:由于竞态条件导致 C1 和 C2 都获取到了锁
幸运的是,可以使用以下的算法来避免这种情况,请看 C4 客户端所使用的好的算法:
C4 发送SETNX lock.foo为了获得该锁
已经崩溃的客户端 C3 仍然持有该锁,所以Redis将会返回0给 C4
C4 发送GET lock.foo检查该锁是否已经过期。如果没有过期,C4 客户端将会睡眠一会,并且从一开始进行重试操作
另一种情况,如果因为 lock.foo键的Unix时间小于当前的Unix时间而导致该锁已经过期,C4 会尝试执行以下的操作:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>
由于GETSET 的语意,C4会检查已经过期的旧值是否仍然存储在lock.foo中。如果是的话,C4 会获得锁
如果另一个客户端,假如为 C5 ,比 C4 更快的通过GETSET操作获取到锁,那么 C4 执行GETSET操作会被返回一个不过期的时间戳。C4 将会从第一个步骤重新开始。请注意:即使 C4 在将来几秒设置该键,这也不是问题。
为了使这种加锁算法更加的健壮,持有锁的客户端应该总是要检查是否超时,保证使用DEL释放锁之前不会过期,因为客户端故障的情况可能是复杂的,不止是崩溃,还会阻塞一段时间,阻止一些操作的执行,并且在阻塞恢复后尝试执行DEL(此时,该LOCK已经被其他客户端所持有)
相关文章
- 实现Redis主从复制:一步一步(redis主从关系)
- Redis 哨兵集群搭建:实现数据的可靠保护(redis哨兵集群搭建)
- Redis 命令指南:最全面的使用手册(redis命令手册)
- Python操作Redis实现数据持久化(python操作redis)
- Redis如何实现高效的分布式锁?(redis的分布式锁)
- 掌握Redis命令集中实现自动执行(执行redis命令)
- 如何检查Redis服务器运行状态(检查redis状态命令)
- 不让Redis在开机立即启动(禁用redis开机启动)
- 探究Redis的SET命令的功能与用法(查看redis命令set)
- 无法入库无法存入Redis(无法存入redis)
- 基于Redis的数据词典设计与实践(数据词典 redis)
- 操作便捷实现Redis批量操作的设置(set redis批量)
- 解锁Redis连接让你拥有无限可能(如何释放redis连接数)
- 大数据智能存储Redis篇(大数据如何存redis)
- 压力Redis集群实现资源压力解放(redis集群释放资源)
- Redis集群常用的数据结构(redis集群常用的结构)
- 深入浅出Redis集群与SET集合(redis集群set集合)
- Redis中集合添加命令的应用(redis 集合添加命令)
- 来Redis队列无法正常启动(redis队列开不起)
- 深度比较Redis锁和ZK锁之间的差异(redis锁和zk锁区别)
- 实例指令练习Redis 连接实例操作指引(redis连接实例命令)