SETNX(3valkey)
π NAME π
setnx β set the string value of a key, but likeβ¦ only if that key doesn't exist yet. it's giving "no takebacks, first come first served" π«Ά
β¨ SYNOPSIS β¨
SETNX key value
πΈ DESCRIPTION πΈ
okay so here's the tea: SETNX sets key to hold the string value if key does not exist. When it does actually set it, it's literally identical to SET. But if key already holds a value? bestie, nothing happens. no operation, no drama, we move on. π
and yes, SETNX is short for "SET if Not eXists" β say it with me now, iykyk β¨
π Alternative π
you can also just use SET with the NX argument. same vibe, different font. π
π REPLY π
you're getting one of these two, no in-between:
valkey-protocol(7)Integer reply β0if the key was not set. πvalkey-protocol(7)Integer reply β1if the key was set. π
β‘ COMPLEXITY β‘
O(1) β constant time, she's fast, she's efficient, she's iconic. π₯
π ACL CATEGORIES π
@fast @string @write
π HISTORY π
- Available since:
1.0.0β an OG, a legend, main character energy since day one. β
π EXAMPLES π
127.0.0.1:6379> SETNX mykey "Hello"
(integer) 1
127.0.0.1:6379> SETNX mykey "World"
(integer) 0
127.0.0.1:6379> GET mykey
"Hello"
see how the second one got hit with that 0? the key was already taken, bestie. "Hello" stays winning. π
π Design pattern: Locking with SETNX π
before we go any further, some housekeeping, and it's important so listen up:
- this pattern is discouraged in favor of
valkey-distlock(7)the Redlock algorithm, which is only a bit more complex to implement but offers better guarantees and is fault tolerant. so like, do that one instead, fr fr. - we're documenting the old pattern anyway because certain existing implementations link to this page as a reference. plus it's a genuinely interesting example of how Valkey commands can be used in order to mount programming primitives. π§
- anyway, even assuming a single-instance locking primitive, starting with
2.6.12it's possible to create a much simpler locking primitive, equivalent to the one discussed here, using theSETcommand to acquire the lock, and a simple Lua script to release the lock. that pattern is documented in theSETcommand page. π
that said β SETNX can be used, and was historically 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. slay. β¨
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. and race conditions are, like, the villain of this story.
C1andC2readlock.footo check the timestamp, because they both received0after executingSETNX, as the lock is still held byC3that crashed after holding the lock.C1sendsDEL lock.fooC1sendsSETNX lock.fooand it succeedsC2sendsDEL lock.fooC2sendsSETNX lock.fooand it succeeds- ERROR: both
C1andC2acquired 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:
C4sendsSETNX lock.fooin order to acquire the lock- the crashed client
C3still holds it, so Valkey will reply with0toC4. C4sendsGET lock.footo 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.foois older than the current Unix time,C4tries to perform:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>
- because of the
GETSETsemantic,C4can 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 thanC4and acquired the lock with theGETSEToperation, theC4GETSEToperation will return a non expired timestamp.C4will simply restart from the first step. note that even ifC4set the key a bit a few seconds in the future this is not a problem. π¦
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). π«Ά
π SEE ALSO π
append(3valkey), decr(3valkey), decrby(3valkey), delifeq(3valkey), get(3valkey), getdel(3valkey), getex(3valkey), getrange(3valkey), getset(3valkey), incr(3valkey), incrby(3valkey), incrbyfloat(3valkey), lcs(3valkey), mget(3valkey), mset(3valkey), msetnx(3valkey), psetex(3valkey), set(3valkey), setex(3valkey), setrange(3valkey), strlen(3valkey), substr(3valkey)
and that's the whole page, bestie!! π this was SETNX(3valkey) from the Valkey Command Manual, version 9.0.0, dated 2025-10-21. go forth and set-if-not-exists responsibly, and remember: if it already exists, it's not your key to set. periodt. π
β¨π
π¦ where this page comes from
- fedora 44: valkey-doc 9.0.3-2.fc44 Β· CC-BY-SA-4.0
- fedora 44: valkey-doc 9.0.6-1.fc44 Β· CC-BY-SA-4.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms