Question

What's the best way to get the sum of SCORES in a Redis sorted set?

Was it helpful?

Solution

The only option I think is iterating the sorted set and computing the sum client side.

OTHER TIPS

Available since Redis v2.6 is the most awesome ability to execute Lua scripts on the Redis server. This renders the challenge of summing up a Sorted Set's scores to trivial:

local sum=0
local z=redis.call('ZRANGE', KEYS[1], 0, -1, 'WITHSCORES')

for i=2, #z, 2 do 
    sum=sum+z[i]
end

return sum

Runtime example:

~$ redis-cli zadd z 1 a 2 b 3 c 4 d 5 e
(integer) 5
~$ redis-cli eval "local sum=0 local z=redis.call('ZRANGE', KEYS[1], 0, -1, 'WITHSCORES') for i=2, #z, 2 do sum=sum+z[i] end return sum" 1 z
(integer) 15

If the sets are small, and you don't need killer performance, I would just iterate (zrange/zrangebyscore) and sum the values client side.

If, on the other hand, you are talking about many thousands - millions of items, you can always keep a reference set with running totals for each user and increment/decrement them as the gifts are sent.

So when you do your ZINCR 123:gifts 1 "3|345", you could do a seperate ZINCR command, which could be something like this:

ZINCR received-gifts 1 <user_id>

Then, to get the # of gifts received for a given user, you just need to run a ZSCORE:

ZSCORE received-gifts <user_id>

Here is a little lua script that maintains the zset score total as you go, in a counter with key postfixed with '.ss'. You can use it instead of ZADD.

local delta = 0
for i=1,#ARGV,2 do
    local oldScore = redis.call('zscore', KEYS[1], ARGV[i+1])
    if oldScore == false then
        oldScore = 0
    end
    delta = delta - oldScore + ARGV[i]
end
local val = redis.call('zadd', KEYS[1], unpack(ARGV))
redis.call('INCRBY', KEYS[1]..'.ss', delta)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top