Jede echte Lösung muss die Anforderungen erfüllen, die in der ursprünglichen Frage irgendwie fehlen. Meine erste Antwort war von einem kleinen Datensatz ausgegangen, aber dieser Ansatz skaliert nicht, da ein dichtes Ranking (z. B. über Lua) mindestens in O (N) erfolgt.
Unter der Annahme, dass es viele Benutzer mit Bewertungen gibt, ist die von for_stack vorgeschlagene Richtung besser, in der mehrere Datenstrukturen kombiniert werden. Ich glaube, das ist der Kern seiner letzten Bemerkung.
Um die Ergebnisse der Benutzer zu speichern, können Sie einen Hash verwenden. Während Sie konzeptionell einen einzigen Schlüssel verwenden können, um einen Hash aller Benutzerergebnisse zu speichern, möchten Sie in der Praxis den Hash hashen, damit er skaliert. Um dieses Beispiel einfach zu halten, ignoriere ich die Hash-Skalierung.
So würden Sie die Punktzahl eines Benutzers in Lua hinzufügen (aktualisieren):
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
Als Nächstes möchten wir die aktuelle Anzahl der Benutzer pro diskretem Score-Wert verfolgen, also behalten wir dafür einen weiteren Hash:
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)
Das letzte, was wir jetzt beibehalten müssen, ist der Rang pro Punktzahl mit einem sortierten Satz. Jede neue Partitur wird als Mitglied im zset hinzugefügt und Partituren, die keine Benutzer mehr haben, werden entfernt:
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
Die Komplexität dieses 3-teiligen Skripts ist O(logN) aufgrund der Verwendung des sortierten Satzes, aber beachten Sie, dass N die Anzahl diskreter Score-Werte ist, nicht die Anzahl der Benutzer im System. Das Abrufen des dichten Rankings eines Benutzers erfolgt über ein anderes, kürzeres und einfacheres Skript:
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)