Solusi nyata apa pun harus sesuai dengan persyaratan, yang agak hilang dalam pertanyaan awal. Jawaban pertama saya mengasumsikan kumpulan data kecil, tetapi pendekatan ini tidak menskala karena peringkat padat dilakukan (misalnya melalui Lua) setidaknya dalam O(N).
Jadi, dengan asumsi bahwa ada banyak pengguna dengan skor, arah yang disarankan for_stack lebih baik, di mana beberapa struktur data digabungkan. Saya percaya ini adalah inti dari komentar terakhirnya.
Untuk menyimpan skor pengguna, Anda dapat menggunakan Hash. Meskipun secara konseptual Anda dapat menggunakan satu kunci untuk menyimpan Hash dari semua skor pengguna, dalam praktiknya Anda ingin hash Hash agar skalanya. Agar contoh ini tetap sederhana, saya akan mengabaikan penskalaan Hash.
Ini adalah bagaimana Anda akan menambahkan (memperbarui) skor pengguna di Lua:
local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)
Selanjutnya, kami ingin melacak jumlah pengguna saat ini per nilai skor diskrit sehingga kami menyimpan hash lain untuk itu:
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)
Sekarang, hal terakhir yang perlu kita pertahankan adalah peringkat per skor, dengan kumpulan yang diurutkan. Setiap skor baru ditambahkan sebagai anggota di zset, dan skor yang tidak memiliki pengguna lagi akan dihapus:
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
Kompleksitas skrip 3-bagian ini adalah O(logN) karena penggunaan Set Terurut, tetapi perhatikan bahwa N adalah jumlah nilai skor diskrit, bukan pengguna dalam sistem. Mendapatkan peringkat padat pengguna dilakukan melalui skrip lain yang lebih pendek dan lebih sederhana:
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)