Раздел 37 · Redis с нуля

Sorted Sets: рейтинги и лидерборды

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

В прошлом уроке мы собрали карточку книги в хеш. Теперь магазину нужна витрина «Самые продаваемые»: десять книг по убыванию продаж, и она меняется с каждой покупкой. Множество из урока 8 не помнит порядка, список из урока 7 не умеет пересортировываться сам. Нужна третья структура.

Турнирная таблица

Вспомни турнирную таблицу футбольной лиги. У каждой команды есть очки, и таблица всегда отсортирована по ним. Команда выиграла матч, ей добавили три очка, и она сама поднялась на нужное место. Никто не переписывает таблицу целиком. По ней легко ответить: кто лидер, на каком месте «Спартак», сколько команд набрали больше 40 очков.

Отсортированное множество в Redis устроено так же. Это множество уникальных строк, но у каждой есть число, его называют весом или очками. Redis держит элементы упорядоченными по весу. Команды этого типа начинаются с буквы Z.

Рейтинг продаж

Кладём в рейтинг четыре книги с числом продаж. ZADD принимает пары: сначала вес, потом элемент:

127.0.0.1:6379> ZADD sales:books 120 book:1 85 book:2 240 book:7 60 book:3
(integer) 4
127.0.0.1:6379> ZADD sales:books 130 book:1
(integer) 0
127.0.0.1:6379> ZCARD sales:books
(integer) 4

Как у обычного множества, элемент встречается один раз. Второй ZADD не добавил книгу заново, а заменил её вес на 130, поэтому ответ 0: новых элементов нет. ZCARD считает элементы.

Обрати внимание: в рейтинге лежат идентификаторы книг, а не карточки. Название и цену приложение возьмёт из хеша book:1 или из базы. Так один объект не хранится в двух местах.

Топ и полная таблица

ZRANGE выдаёт элементы по позициям, от меньшего веса к большему. Позиции считаются с нуля, -1 значит «до конца», как в LRANGE. Флаг WITHSCORES добавляет веса:

127.0.0.1:6379> ZRANGE sales:books 0 -1 WITHSCORES
1) "book:3"
2) "60"
3) "book:2"
4) "85"
5) "book:1"
6) "130"
7) "book:7"
8) "240"

Для витрины нужен обратный порядок, от лидера вниз. Его даёт флаг REV. Тройка лидеров:

127.0.0.1:6379> ZRANGE sales:books 0 2 REV WITHSCORES
1) "book:7"
2) "240"
3) "book:1"
4) "130"
5) "book:2"
6) "85"

В старых примерах встретится команда ZREVRANGE, она делает то же самое. Начиная с Redis 6.2 всё собрали в ZRANGE с флагами.

Продажа одной книги

Купили ещё 15 экземпляров «Собачьего сердца». ZINCRBY прибавляет к весу и сразу пересортировывает:

127.0.0.1:6379> ZINCRBY sales:books 15 book:2
"100"
127.0.0.1:6379> ZSCORE sales:books book:7
"240"
127.0.0.1:6379> ZSCORE sales:books book:5
(nil)

Ответ приходит строкой, потому что вес это дробное число. ZSCORE читает вес одного элемента, а для книги, которой нет в рейтинге, отвечает (nil). Если ZINCRBY вызвать для книги, которой ещё нет, Redis добавит её с весом, равным шагу. Как и INCR, команда атомарная: сто одновременных покупок дадут ровно +100.

урок из курса

Урок из курса «Redis с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.