Qualsiasi soluzione reale deve soddisfare i requisiti, che mancano in qualche modo nella domanda originale. La mia prima risposta aveva presupposto un piccolo set di dati, ma questo approccio non è scalabile poiché viene eseguita una classificazione densa (ad esempio tramite Lua) almeno in O(N).
Quindi, supponendo che ci siano molti utenti con punteggi, la direzione suggerita da for_stack è migliore, in cui vengono combinate più strutture di dati. Credo che questo sia il succo della sua ultima osservazione.
Per memorizzare i punteggi degli utenti puoi utilizzare un hash. Mentre concettualmente puoi usare una singola chiave per memorizzare un hash di tutti i punteggi degli utenti, in pratica vorresti eseguire l'hash dell'hash in modo che si ridimensioni. Per semplificare questo esempio, ignorerò il ridimensionamento hash.
Ecco come aggiungere (aggiornare) il punteggio di un utente in Lua:
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
Successivamente, vogliamo tenere traccia del conteggio corrente di utenti per valore di punteggio discreto in modo da mantenere un altro hash per questo:
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)
Ora, l'ultima cosa che dobbiamo mantenere è la classifica per punteggio, con un set ordinato. Ogni nuovo punteggio viene aggiunto come membro nello zset e i punteggi che non hanno più utenti vengono rimossi:
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
La complessità di questo script in 3 parti è O(logN) a causa dell'uso dell'insieme ordinato, ma si noti che N è il numero di valori di punteggio discreti, non gli utenti nel sistema. Ottenere il ranking denso di un utente avviene tramite un altro script, più breve e più semplice:
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)