実際のソリューションは、元の質問にはない要件に適合する必要があります。私の最初の答えは小さなデータセットを想定していましたが、少なくともO(N)で密なランキングが行われるため(たとえば、Luaを介して)、このアプローチは拡張できません。
したがって、スコアを持つユーザーが多数いると仮定すると、for_stackが提案した方向は、複数のデータ構造が組み合わされた方が適切です。これが彼の最後の発言の要点だと思います。
ユーザーのスコアを保存するには、ハッシュを使用できます。概念的には、単一のキーを使用してすべてのユーザースコアのハッシュを保存できますが、実際には、スケーリングするようにハッシュをハッシュする必要があります。この例を簡単にするために、ハッシュスケーリングは無視します。
これは、Luaでユーザーのスコアを追加(更新)する方法です:
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
次に、個別のスコア値ごとの現在のユーザー数を追跡して、そのための別のハッシュを保持します。
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)
さて、私たちが維持する必要がある最後のことは、ソートされたセットを使用したスコアごとのランクです。新しいスコアはすべてzsetのメンバーとして追加され、ユーザーがいないスコアは削除されます:
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
ソートされたセットを使用するため、この3ピーススクリプトの複雑さはO(logN)ですが、Nはシステム内のユーザーではなく、個別のスコア値の数であることに注意してください。ユーザーの密なランキングを取得するには、別の短くて単純なスクリプトを使用します。
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)