Enhver reel løsning skal passe til kravene, som lidt mangler i det originale spørgsmål. Mit 1. svar havde antaget et lille datasæt, men denne tilgang skalerer ikke, da tæt rangering udføres (f.eks. via Lua) i O(N) i det mindste.
Så hvis man antager, at der er mange brugere med score, er den retning, som for_stack foreslog, bedre, hvor flere datastrukturer kombineres. Jeg tror, at dette er essensen af hans sidste bemærkning.
For at gemme brugernes score kan du bruge en Hash. Mens du konceptuelt kan bruge en enkelt tast til at gemme en Hash af alle brugeres score, vil du i praksis ønske at hash Hash, så den skaleres. For at holde dette eksempel simpelt, vil jeg ignorere Hash-skalering.
Sådan tilføjer (opdaterer) en brugers score i Lua:
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
Dernæst vil vi spore det aktuelle antal brugere pr. diskret scoreværdi, så vi beholder endnu en hash for det:
local old_score = new_score - increment
local hcounts_key = KEYS[2]
local old_count = redis.call('HINCRBY', hcounts_key, old_score, -1)
local new_count = redis.call('HINCRBY', hcounts_key, new_score, 1)
Nu er den sidste ting, vi skal vedligeholde, rangeringen pr. score, med et sorteret sæt. Hver ny score tilføjes som medlem i zset, og score, der ikke har flere brugere, fjernes:
local zdranks_key = KEYS[3]
if new_count == 1 then
redis.call('ZADD', zdranks_key, new_score, new_score)
end
if old_count == 0 then
redis.call('ZREM', zdranks_key, old_score)
end
Dette 3-delte scripts kompleksitet er O(logN) på grund af brugen af det sorterede sæt, men bemærk, at N er antallet af diskrete scoreværdier, ikke brugerne i systemet. At få en brugers tætte rangering sker via et andet, kortere og enklere script:
local hscores_key = KEYS[1]
local zdranks_key = KEYS[2]
local user = ARGV[1]
local score = redis.call('HGET', hscores_key, user)
return redis.call('ZRANK', zdranks_key, score)