Elke echte oplossing moet voldoen aan de vereisten, die een beetje ontbreken in de oorspronkelijke vraag. Mijn eerste antwoord was uitgegaan van een kleine dataset, maar deze benadering schaalt niet, aangezien er in ieder geval een dichte rangschikking wordt gedaan (bijvoorbeeld via Lua) in O(N).
Dus, ervan uitgaande dat er veel gebruikers zijn met scores, is de richting die for_stack suggereert beter, waarin meerdere datastructuren worden gecombineerd. Ik geloof dat dit de kern is van zijn laatste opmerking.
Om de scores van gebruikers op te slaan, kunt u een hash gebruiken. Hoewel je conceptueel een enkele sleutel kunt gebruiken om een hash van alle gebruikersscores op te slaan, zou je in de praktijk de hash willen hashen zodat deze zal schalen. Om dit voorbeeld eenvoudig te houden, negeer ik Hash-schaling.
Zo zou u de score van een gebruiker in Lua toevoegen (bijwerken):
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
Vervolgens willen we het huidige aantal gebruikers per afzonderlijke scorewaarde bijhouden, dus daar houden we nog een hash voor:
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)
Het laatste dat we moeten handhaven, is de rangorde per score, met een gesorteerde set. Elke nieuwe score wordt toegevoegd als lid in de zset, en scores die geen gebruikers meer hebben worden verwijderd:
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
De complexiteit van dit driedelige script is O(logN) vanwege het gebruik van de gesorteerde verzameling, maar merk op dat N het aantal discrete scorewaarden is, niet de gebruikers in het systeem. Het verkrijgen van de hoge ranking van een gebruiker wordt gedaan via een ander, korter en eenvoudiger 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)