Jakékoli skutečné řešení musí odpovídat požadavkům, které v původní otázce jaksi chybí. Moje první odpověď předpokládala malý soubor dat, ale tento přístup se neškáluje, protože se provádí husté řazení (např. přes Lua) alespoň v O(N).
Takže za předpokladu, že existuje mnoho uživatelů se skóre, směr navrhovaný for_stack je lepší, ve kterém je kombinováno více datových struktur. Věřím, že toto je podstata jeho poslední poznámky.
Chcete-li uložit skóre uživatelů, můžete použít hash. Zatímco koncepčně můžete použít jeden klíč k uložení hash skóre všech uživatelů, v praxi byste chtěli hash hash, aby se škáloval. Aby byl tento příklad jednoduchý, budu ignorovat škálování hash.
Takto přidáte (aktualizujete) skóre uživatele v Lua:
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
Dále chceme sledovat aktuální počet uživatelů na jednotlivé hodnoty skóre, takže si pro to ponecháváme další 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)
Nyní poslední věc, kterou musíme zachovat, je pořadí podle skóre se seřazenou sadou. Každé nové skóre je přidáno jako člen do zset a skóre, které nemá žádné další uživatele, je odstraněno:
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
Složitost tohoto třídílného skriptu je O(logN) kvůli použití Sorted Set, ale všimněte si, že N je počet diskrétních hodnot skóre, nikoli uživatelů v systému. Získání hustého hodnocení uživatele se provádí pomocí jiného, kratšího a jednoduššího skriptu:
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)