本文内容为 http://redis.io/commands/setnx 的翻译、注解、例子扩充及其它修改。感谢 Redis 作者 Antirez 为开源社区作出的贡献,本文保证最新、最准、最全以表示对其的敬意。欢迎留言纠错、提示更新或支持。
SETNX key value

当键 key 不存在时,将被设置为字符串值(String value)value。在该情况下,它等同于 SET 命令。

Set key to hold string value if key does not exist. In that case, it is equal to SET.

当键 key 已经存在,将不进行任何操作。

When key already holds a value, no operation is performed.

命令 SETNX 可以认为是 “SET if N ot e X ists” 的缩写。

    • 如果键 key 被设置,则返回 1
    • 如果键 key 未被设置,则返回 0

    1 if the key was set
    0 if the key was not set

  • redis> SETNX mykey "Hello"
    (integer) 1
    redis> SETNX mykey "World"
    (integer) 0
    redis> GET mykey
    "Hello"
  • 可以用 SETNX 来实现锁机制

    注意2.6.12 版本开始,实现锁原语(Locking primitive)将会变得更加简单,通过 SET 命令实现锁获取(Acquire the lock),然后通过一个简单的 Lua 脚本来实现锁释放(Release the lock)

    NOTE: Starting with Redis 2.6.12 it is possible to create a much simpler locking primitive using the SET command to acquire the lock, and a simple Lua script to release the lock. The pattern is documented in the SET command page.

    The old SETNX based pattern is documented below for historical reasons.

    SETNX can be used as a locking primitive. For example, to acquire the lock of the key foo, the client could try the following:

    SETNX lock.foo <current Unix time + lock timeout + 1>

    If SETNX returns 1 the client acquired the lock, setting the lock.foo key to the Unix time at which the lock should no longer be considered valid. The client will later use DEL lock.foo in order to release the lock.

    If SETNX returns 0 the key is already locked by some other client. We can either return to the caller if it's a non blocking lock, or enter a loop retrying to hold the lock until we succeed or some kind of timeout expires.


    处理死锁

    Handling deadlocks

    In the above locking algorithm there is a problem: what happens if a client fails, crashes, or is otherwise not able to release the lock? It's possible to detect this condition because the lock key contains a UNIX timestamp. If such a timestamp is equal to the current Unix time the lock is no longer valid.

    When this happens we can't just call DEL against the key to remove the lock and then try to issue a SETNX, as there is a race condition here, when multiple clients detected an expired lock and are trying to release it.

    • C1 and C2 read lock.foo to check the timestamp, because they both received 0 after executing SETNX, as the lock is still held by C3 that crashed after holding the lock.
    • C1 sends DEL lock.foo
    • C1 sends SETNX lock.foo and it succeeds
    • C2 sends DEL lock.foo
    • C2 sends SETNX lock.foo and it succeeds
    • ERROR: both C1 and C2 acquired the lock because of the race condition.

    Fortunately, it's possible to avoid this issue using the following algorithm. Let's see how C4, our sane client, uses the good algorithm:

    • C4 sends SETNX lock.foo in order to acquire the lock
    • The crashed client C3 still holds it, so Redis will reply with 0 to C4.
    • C4 sends GET lock.foo to check if the lock expired. If it is not, it will sleep for some time and retry from the start.
    • Instead, if the lock is expired because the Unix time at lock.foo is older than the current Unix time, C4 tries to perform:
    GETSET lock.foo <current Unix timestamp + lock timeout + 1>
    • Because of the GETSET semantic, C4 can check if the old value stored at key is still an expired timestamp. If it is, the lock was acquired.
    • If another client, for instance C5, was faster than C4 and acquired the lock with the GETSET operation, the C4GETSET operation will return a non expired timestamp. C4 will simply restart from the first step. Note that even if C4 set the key a bit a few seconds in the future this is not a problem.

    Important note: In order to make this locking algorithm more robust, a client holding a lock should always check the timeout didn't expire before unlocking the key with DEL because client failures can be complex, not just crashing but also blocking a lot of time against some operations and trying to issue DEL after a lot of time (when the LOCK is already held by another client).

  • 版本支持

    1.0.0+

    时间复杂度(Time complexity)

    O(1)