Как реализовать список лидеров соревнований, используя отсортированный набор redis - PullRequest
0 голосов
/ 19 февраля 2020

Я использую отсортированный по Redis набор, чтобы поддерживать таблицу лидеров в моей игре. У меня есть сценарий, в котором мне нужно поддерживать тот же рейтинг пользователей, которые имеют тот же счет, что и таблица лидеров соревнований. Например,

| member | score | rank |
— — — — — — — — — — —
| member_1 | 50 | 1 |
| member_2 | 50 | 1 |
| member_3 | 30 | 3 |
| member_4 | 30 | 3 |
| member_5 | 10 | 5 |

На данный момент я использую стандартную реализацию отсортированного набора Redis, который возвращает рейтинг лексикографически.

127.0.0.1:6379> zadd test-leaderboard 9 user1
(integer) 1
127.0.0.1:6379> zadd test-leaderboard 5 user2
(integer) 1
127.0.0.1:6379> zadd test-leaderboard 5 user3
(integer) 1
127.0.0.1:6379> zadd test-leaderboard 3 user4
(integer) 1

Если я запрашиваю рейтинг user2 и user3, я получить другой результат

127.0.0.1:6379> zrank test-leaderboard user2
(integer) 1
127.0.0.1:6379> zrank test-leaderboard user3
(integer) 2

Я проверил документацию Redis, такой функции для этого не существует. Поэтому я хочу знать, что мне нужно сделать или как лучше всего реализовать эту функцию.

Примечание: У меня есть 10K записей в SET, и мне нужно поддерживать их во время выполнения и Я использую Java язык программирования.

1 Ответ

1 голос
/ 19 февраля 2020

Сортированные наборы сортируются сначала по баллам, а затем лексикографически, поэтому вы получаете различный ранг для user2 и user3.

Вы можете комбинировать ZSCORE, ZRANGEBYSCORE и ZRANK нормализовать это. По сути, вы получаете оценку за user3, затем вводите лексикографически первого пользователя в ie и получаете ранг для этого пользователя.

> ZSCORE test-leaderboard user3
"5"
> ZRANGEBYSCORE test-leaderboard 5 5 LIMIT 0 1
1) "user2"
> ZRANK test-leaderboard user2
(integer) 1

Это дает вам ранг, а связи оценивают то же самое, но оставляя пробелы для ранжирования.

user4 => 0
user2 => 1
user3 => 1
user1 => 3

Если вы хотите, чтобы ваши ранги были без пробелов, вы можете либо сохранить таблицу лидеров со списком пользователей с заданным баллом за запись (ZADD test-leaderboard 5 "user2,user3"), либо сохранить отдельный отсортированный набор только с уникальными баллами. Я бы go со вторым по эффективности.

Добавление нового игрока [O(log(N))]:

ZADD test-leaderboard 5 user2
ZADD test-ranks 5 5

Удаление игрока [O(log(N))]:

> ZSCORE test-leaderboard user2
"5"
> ZREM test-leaderboard user2
(integer) 1
> ZRANGEBYSCORE test-leaderboard 5 5 LIMIT 0 1
1) "anotherUser" or (empty list or set)
if(empty set)
> ZREM test-ranks 5

Обновить счет игрока [O(log(N))]:

> ZSCORE test-leaderboard user2
"5"
> ZADD test-leaderboard 10 user2
(integer) 1
> ZADD test-ranks 10 10
(integer) 1
> ZRANGEBYSCORE test-leaderboard 5 5 LIMIT 0 1
1) "anotherUser" or (empty list or set)
if(empty set)
> ZREM test-ranks 5

Получить звание игрока [O(log(N))]:

> ZSCORE test-leaderboard user2
"5"
> ZRANK test-ranks 5
(integer) 1

Пара нот:

Используйте ZREVXXX команды, если высший балл имеет высший ранг .

ZRANK занимает наименьшее количество баллов первым, используйте ZREVRANK, если хотите, чтобы наивысший балл был на первом месте. См. ZREVRANK и ZREVRANGEBYSCORE.

Использование Lua сценариев

С Lua scripts , вы можете сделать ваши операции атомами c и выполнять их быстрее.

Вот пример. Вместо

> ZSCORE test-leaderboard user2
"5"
> ZRANK test-ranks 5
(integer) 1

Используйте скрипт:

local score = redis.call('ZSCORE', KEYS[1], ARGV[1])
return redis.call('ZRANK', KEYS[2], score)

Используйте как:

> EVAL "local score = redis.call('ZSCORE', KEYS[1], ARGV[1]) \n return redis.call('ZRANK', KEYS[2], score)" 2 test-leaderboard test-ranks user2
(integer) 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...