SETNX (已弃用)
从 Redis 版本 2.6.12 开始,此命令被视为已弃用。
它可以替换为SET使用NX参数。
SETNX key value
- 从以下位置开始可用:
- 1.0.0
- 时间复杂度:
- O(1)
- ACL 类别:
-
@write,@string,@fast,
设置key保持字符串value如果key不存在。
在这种情况下,它等于SET.
什么时候keyalready hold a 值,则不执行任何作。SETNX是 “SET if Not eXists” 的缩写。
例子
设计模式:锁定方式SETNX
请注意:
- 不建议使用以下模式,而使用 Redlock 算法,该算法的实现稍微复杂一些,但提供了更好的保证并且具有容错能力。
- 无论如何,我们都会记录旧模式,因为某些现有的实现会链接到此页面作为参考。此外,这是一个有趣的例子,说明如何使用 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密钥已被其他客户端锁定。
如果是非阻塞锁,我们可以返回给调用者,也可以进入循环
重试持有锁,直到我们成功或某种超时过期。
处理死锁
在上面的锁定算法中,有一个问题:如果客户端 失败、崩溃或无法释放锁? 之所以能够检测到这种情况,是因为 lock key 包含 UNIX 时间戳。 如果这样的时间戳等于当前 Unix 时间,则锁不再是 有效。
当这种情况发生时,我们不能只调用DEL对着钥匙去锁
,然后尝试发出SETNX,因为这里存在争用条件,因此当
多个客户端检测到过期的锁并尝试释放它。
- C1 和 C2 读取
lock.foo来检查时间戳,因为他们都收到了0执行后SETNX,因为锁仍然由崩溃的 C3 持有 在持有锁后。 - C1 发送
DEL lock.foo - C1 发送
SETNX lock.foo它成功了 - C2 发送
DEL lock.foo - C2 发送
SETNX lock.foo它成功了 - 错误:由于争用条件,C1 和 C2 都获取了锁。
幸运的是,使用以下算法可以避免此问题。 让我们看看我们的 sane 客户端 C4 如何使用良好的算法:
-
C4 发送
SETNX lock.foo为了获取锁 -
崩溃的客户端 C3 仍然持有它,因此 Redis 将回复
0到 C4。 -
C4 发送
GET lock.foo检查锁是否过期。 如果不是,它将休眠一段时间,然后从头开始重试。 -
相反,如果锁已过期,因为 Unix 时间在
lock.foo年龄较大 C4 尝试执行:GETSET lock.foo <current Unix timestamp + lock timeout + 1> -
由于
GETSETsemantic 中,C4 可以检查存储在key仍为过期时间戳。 如果是,则已获取锁。 -
如果另一个客户端(例如 C5)比 C4 更快并获取了锁 使用
GETSET作, C4GETSET作将返回非 expired 时间戳。 C4 将简单地从第一步重新启动。 请注意,即使 C4 将密钥设置在将来几秒钟后,这也是 没问题。
为了使此锁定算法更加健壮,
持有锁的客户端应始终检查 timeout didn't expiration before
解锁密钥DEL因为客户端故障可能很复杂,而不仅仅是
崩溃但也阻止了大量时间来对抗某些作和尝试
发行DEL经过大量时间后(当 LOCK 已由另一个人持有时
client) 的
RESP2/RESP3 回复
以下选项之一: