Każde prawdziwe rozwiązanie musi odpowiadać wymaganiom, których brakuje w pierwotnym pytaniu. Moja pierwsza odpowiedź zakładała mały zestaw danych, ale to podejście nie skaluje się, ponieważ robi się gęsty ranking (np. przez Lua) przynajmniej w O(N).
Tak więc, zakładając, że jest wielu użytkowników z wynikami, lepszy jest kierunek sugerowany przez for_stack, w którym łączy się wiele struktur danych. Wierzę, że to jest sedno jego ostatniej uwagi.
Do przechowywania wyników użytkowników możesz użyć skrótu. Chociaż koncepcyjnie można użyć jednego klucza do przechowywania wartości skrótu wszystkich użytkowników, w praktyce warto haszować, aby był on skalowany. Aby ten przykład był prosty, zignoruję skalowanie skrótu.
W ten sposób możesz dodać (zaktualizować) wynik użytkownika w Lua:
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
Następnie chcemy śledzić bieżącą liczbę użytkowników na dyskretną wartość wyniku, więc przechowujemy w tym celu kolejny skrót:
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)
Teraz ostatnią rzeczą, którą musimy utrzymywać, jest ranking na wynik, z posortowanym zestawem. Każda nowa partytura jest dodawana jako członek zset, a partytury, które nie mają więcej użytkowników, są usuwane:
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
Złożoność tego 3-częściowego skryptu wynosi O(logN) ze względu na użycie posortowanego zbioru, ale zauważ, że N to liczba dyskretnych wartości punktacji, a nie użytkowników w systemie. Uzyskiwanie gęstego rankingu użytkownika odbywa się za pomocą innego, krótszego i prostszego skryptu:
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)