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 mykey “你好” SETNX mykey “世界” 获取 mykey

设计模式:锁定方式SETNX

请注意:

  1. 不建议使用以下模式,而使用 Redlock 算法,该算法的实现稍微复杂一些,但提供了更好的保证并且具有容错能力。
  2. 无论如何,我们都会记录旧模式,因为某些现有的实现会链接到此页面作为参考。此外,这是一个有趣的例子,说明如何使用 Redis 命令来挂载编程原语。
  3. 无论如何,即使假设一个单实例锁定原语,从 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 回复

以下选项之一:


为本页评分
返回顶部 ↑