I would like to increase a value only if a certain key exists. If the key does not exist, the value should be set to 1. Is it possible to implement this logic as an atomic step?
The INCR command does exactly what you want.
If the key does NOT exist, the value will be set to 0 before performing the increase operation, i.e. the value will be set to 1. And the command runs atomically.
UPDATE
If the key to be increased is NOT the same key whose existence is checked, you have to write a Lua script to do the work in a transaction.
if (redis.call("exists", KEYS[1]) > 0) then redis.call("incr", KEYS[2]) end
Related
I have a dozen of REDIS Keys of the type SET, say
PUBSUB_USER_SET-1-1668985588478915880,
PUBSUB_USER_SET-2-1668985588478915880,
PUBSUB_USER_SET-3-1668988644477632747,
.
.
.
.
PUBSUB_USER_SET-10-1668983464477632083
The set contains a userId and the problem statement is to check if the user is present in any of the set or not
The solution I tried is to get all the keys and append with a delimiter (, comma) and pass it as an argument to lua script wherein with gmatch operator I split the keys and run sismember operation until there is a hit.
local vals = KEYS[1]
for match in (vals..","):gmatch("(.-)"..",") do
local exist = redis.call('sismember', match, KEYS[2])
if (exist == 1) then
return 1
end
end
return 0
Now as and when the number of keys grows to PUBSUB_USER_SET-20 or PUBSUB_USER_SET-30 I see an increase in latency and in throughput.
Is this the better way to do or Is it better to batch LUA scripts where in instead of passing 30keys as arguments I pass in batches of 10keys and return as soon as the user is present or is there any better way to do this?
I would propose a different solution instead of storing keys randomly in a set. You should store keys in one set and you should query that set to check whether a key is there or not.
Lets say we've N sets numbered s-0,s-1,s-2,...,s-19
You should put your keys in one of these sets based on their hash key, which means you need to query only one set instead of checking all these sets. You can use any hashing algorithm.
To make it further interesting you can try consistent hashing.
You can use redis pipeline with batching(10 keys per iteration) to improve the performance
I want to increment a redis counter but I want to start counting not from zero but from a defined starting number (for example -5).
I know how this can be achieved via SET/INCR inside a Lua script but I was wondering if I can achieve it only with INCR command. Something similar we define for INCRBY where the increment is defined, can we define the starting point?
Lua is perfectly fine for this procedure, but you can also do it with a transaction:
MULTI
SET counter -5 NX
INCR counter
EXEC
The INCR will run every time, so if you want your first call to set it to -5 you should change the SET value to -6. You can also pipe those 4 commands to avoid the 4 RTTs of sending the commands.
You can't do it with the INCR command alone. I would inspect the value of SETNX and if it returns 0 (meaning the key existed), then increment it.
Notice that if you are talking about non expiring counters, you can achieve atomicity this way without Lua, at the price of two roundtrips: If the key did not exist, we create it, set it to the initial value and that's it, one roundtrip, atomic. If it did exist, we increment it, but we are still consistent (unless the key expired or was deleted between the two calls).
However, there is no reason not to use a Lua script for this, it's the preferred way to do this stuff.
I want to use Redis as a random seed cache. When I want the value for a key, if nothing's there yet, I'll produce a random string and store it for later reuse.
How do I perform an atomic GET EXISTING OR SET AND RETURN THIS VALUE?
You could use SETNX to try and set the value first. Then the GET would give you the existing value or the new one you tried to set.
SETNX key value
This may return 0 or 1 if you care to know if this is a new value
It seems there is no single command that can do this. Using MULTI and WATCH:
First:
GET key
If null, then:
WATCH key
MULTI
SET key value
EXEC
If [null] (indicating the transaction aborted), the key was created in the meantime and must exist by now:
GET key
I'm using Redis to store a set of checksums. I'm incrementing each member that I see while parsing a large dataset, and using the score to determine which I've "visited" more than once. However, as this operation is done periodically, I would like to reset the scores of all members to zero afterward. Is there a good way of doing this?
I am aware of ZRANGEBYSCORE, and perhaps could "copy" what it returns into a new key, however with a large set of data, this is less desirable. I could also take the minimum score at the start of the process and ZREMRANGEBYSCORE everything at or below that score, but this too seems undesirable as my scores would continue to rise indefinitely.
You could use ZUNIONSTORE with a WEIGHTS value of zero to copy the set and zero all scores, e.g.
ZUNIONSTORE myZeroedSet 1 myInitialSet WEIGHTS 0
RENAME myZeroedSet myInitialSet
A weight of zero will cause all scores in myInitialSet to be multiplied by zero, thus resetting them. The RENAME will succeed without you having to explicitly delete myInitialSet first.
The advantage of this is that it happens inside Redis (you don't have to transfer the entire set over the network and back), and it's fast.
The best and quickest way to do that is using a simple LUA script :
eval "local z=redis.call('ZRANGE', KEYS[1], 0, -1) for i=1, #z, 1 do redis.call('ZADD', KEYS[1], 0, z[i]) end" 1 myInitialSet
It's atomic and everything happens inside Redis too.
I am interested in creating several different redis based counters in my web application. A lot of this stuff is basically for metrics etc, but that doesn't make a difference. My question is essentially the following, is it possible to avoid doing:
if $redis.get(key) != null
// increment key
else
// create key with a counter of 1
Ideally something like this would be more optimal
$redis.incr(key, 1) // increment key by 1, and if it does not exist, start it at the value 1
am I overlooking the redis documentation? Is there a way to do this currently?
there is a INCR command, which if the key does not exists sets the value of the key to 1
$redis.incr()
should work.
see http://redis.io/commands/incr