sql >> Base de Datos >  >> NoSQL >> Redis

¿Cómo obtener el mismo rango para las mismas puntuaciones en ZRANK de Redis?

Cualquier solución real debe cumplir con los requisitos, que faltan en la pregunta original. Mi primera respuesta había asumido un conjunto de datos pequeño, pero este enfoque no se escala a medida que se realiza una clasificación densa (por ejemplo, a través de Lua) en O (N) al menos.

Entonces, suponiendo que hay muchos usuarios con puntajes, la dirección sugerida por for_stack es mejor, en la que se combinan múltiples estructuras de datos. Creo que esta es la esencia de su último comentario.

Para almacenar las puntuaciones de los usuarios, puede utilizar un Hash. Si bien, conceptualmente, puede usar una sola clave para almacenar un hash de las puntuaciones de todos los usuarios, en la práctica, querrá codificar el hash para que se escale. Para simplificar este ejemplo, ignoraré el escalado Hash.

Así es como agregaría (actualizaría) la puntuación de un usuario 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)

A continuación, queremos realizar un seguimiento del recuento actual de usuarios por valor de puntuación discreta, por lo que mantenemos otro hash para eso:

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)

Ahora, lo último que debemos mantener es el rango por puntaje, con un conjunto ordenado. Cada nueva partitura se agrega como miembro en el zset, y las partituras que no tienen más usuarios se eliminan:

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 complejidad de este script de 3 piezas es O(logN) debido al uso del conjunto ordenado, pero tenga en cuenta que N es el número de valores de puntuación discretos, no los usuarios en el sistema. Obtener la clasificación densa de un usuario se realiza a través de otro script más corto y 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)