Redis
 sql >> Base de données >  >> NoSQL >> Redis

Comment obtenir le même classement pour les mêmes scores dans le ZRANK de Redis ?

Toute solution réelle doit répondre aux exigences, qui manquent un peu dans la question d'origine. Ma 1ère réponse avait supposé un petit ensemble de données, mais cette approche ne s'adapte pas car un classement dense est effectué (par exemple via Lua) en O(N) au moins.

Donc, en supposant qu'il y ait beaucoup d'utilisateurs avec des scores, la direction suggérée par for_stack est meilleure, dans laquelle plusieurs structures de données sont combinées. Je crois que c'est l'essentiel de sa dernière remarque.

Pour stocker les scores des utilisateurs, vous pouvez utiliser un hachage. Alors que conceptuellement, vous pouvez utiliser une seule clé pour stocker un hachage de tous les scores des utilisateurs, en pratique, vous voudriez hacher le hachage pour qu'il évolue. Pour que cet exemple reste simple, je vais ignorer la mise à l'échelle du hachage.

Voici comment ajouter (mettre à jour) le score d'un utilisateur en Lua :

local hscores_key = KEYS[1]
local user = ARGV[1]
local increment = ARGV[2]
local new_score = redis.call('HINCRBY', hscores_key, user, increment)

Ensuite, nous voulons suivre le nombre actuel d'utilisateurs par valeur de score discrète, nous gardons donc un autre hachage pour cela :

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)

Maintenant, la dernière chose que nous devons maintenir est le classement par score, avec un ensemble trié. Chaque nouveau score est ajouté en tant que membre dans le zset, et les scores qui n'ont plus d'utilisateurs sont supprimés :

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

La complexité de ce script en 3 parties est O(logN) en raison de l'utilisation de l'ensemble trié, mais notez que N est le nombre de valeurs de score discrètes, et non les utilisateurs du système. L'obtention du classement dense d'un utilisateur se fait via un autre script plus court et plus simple :

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)