Некоторые структуры данных Redis

Некоторые структуры данных Redis

Роман Алексеев

Обычно все используют Redis как кэш. Присваивают ключу значение, читают значение по ключу. Максимум - присваивают время жизни ключа. Но когда у всех всё одинаково - скучно. Поэтому расскажу про некоторые другие структуры данных в Redis.

К тому же Redis Labs снес раздел сайта с оригинальной документацией от автора.


Hash

Словарь.

Реализуется как хэш-таблица либо (для маленьких хэшей) как упакованный бинарный формат, по смыслу напоминающий "количество:ключ:значение:ключ:значение".

Варианты использования:


1) Сохраняем некий объект целиком по одному ключу, читаем весь объект или часть полей объекта по ключ.

hset cusomer_1 id 123 name Alice age 15

hgetall customer_1 или hmget customer_1 name age или hget customer_id age

Объект будет найден за O(1), а в БД пришлось бы искать в индексе за O(log n).


2) Очень быстро за O(1) ищем во множестве значений по ключу.

Например, строим хэш вида email:id и заполняем его миллионами таких пар.

При логине в приложении мгновенно находим идентификатор пользователя.

hset user_logins alice@mail.ru 1 bob@mail.ru 2 charlie@mail.ru 3 david@mail.ru 4 eve@mail.ru 6 frank@mail.ru 7

hget user_logins charlie@mail.ru // возвращает 3


Либо строим хэш вида короткая_ссылка:длинная_ссылка и получаем тот же эффект (константное время) для хранилища коротких ссылок.

hset short_long_links abc github.com/qwer xyz stackoverflow.com/asdf

hget short_long_links abc // возвращает github.com/qwer

В обоих случаях строковый индекс в БД работал бы существенно медленнее за O(log n).


List

Связанный список.

Реализуется как связанный список массивов.

Варианты использования:


1) Выводим набор "последних" записей, например записей в блоге, комментариев и т.д.

Добавляем в список идентификаторы записей в порядке поступления. Это не хэш, ключей и значений здесь нет. Затем берем 3 последних записи.

rpush article_ids 1 2 3 4 5 6 7 8 9 // добавляем в конец списка

lrange article_ids -3 -1 // возвращает 7 8 9 - три статьи из конца списка


2) Храним только несколько последних записей. Например последние добавленные пользователи.

При добавлении пользователей, добавляем их имена в список. После каждого добавления сразу отрезаем от списка всех, кроме N последних.

rpush latest_users alice bob charlie david eve frank // добавляем в конец списка

ltrim latest_users -3 -1 // обрезаем начало списка кроме 3х последних

lrange latest_users 0 -1 // смотрим весь список, в нем только david eve frank


3) Реализуем очередь. Добавляем команды в список, затем поочередно получаем и удаляем полученные команды из списка.

rpush commands first second third // добавляем команды в конец списка

lpop commands 1 // за O(1) вернет и удалит first, в списке останется second, third


Set

Множество уникальных элементов.

Пока множество маленькое, его реализация больше напоминает массив, когда множество вырастает - преращается в хэш-таблицу.

Варианты использования:


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

sadd page_1_viewers_2024-11-01 alice bob charlie charlie charlie // записываем пользователя каждый раз, когда он заходит на страницу

smembers page_1_viewers_2024-11-01 // вернет alice bob charlie


2) Находим общие элементы множеств. Например, общих друзей в социальной сети.

sadd alice_friends charlie david frank // друзья Алисы в первом множестве

sadd bob_friends david eve frank // Друзья Боба во втором множестве

sinter alice_friends bob_friends // возвращает общих друзей david frank

Работает и для нескольких множеств одной командой.


3) Находим различающиеся элементы множеств. Например чего не хватает игроку для завершения миссии.

sadd mission_requirements steal_artifact_1 open_door_1 open_door_2 // что нужно сделать

sadd player_1_achievements steal_artifact_1 open_door_1 // что сделано

sdiff mission_requirements player_1_achievements // вернет open_door_2 - что осталось сделать


4) Получаем случайный элемент множества.

Например, выбираем победителя лотереи.

sadd users alice bob charlie david eve frank

srandmember users // возвращает случайного пользователя



Sorted Set

Множество, элементы в котором отсортированы по весу (score).

Реализовано как комбинация хэш-таблицы и структуры данных SkipList (детали в статье).

Варианты использования:


1) Выводим несколько максимальных или минимальных элементов по весу. Например, топ рекордов.

Когда пилот Формулы-1 завершает круг, можно сохранить его время и затем вывести топ.

zadd track_1_totals 533.64 pilot_1 525.22 pilot_2 523.87 pilot_3 520.03 pilot_4 530.12 pilot_5

zrange track_1_totals 0 2 withscores // вернет троих лучших pilot_4 520.03 pilot_3 523.87 pilot_2 525.22 и их результаты.

Или можно узнать на каком месте находится один из пилотов .

zrank track_1_totals pilot_1 withscore // возвращает 4 и его результат 533.64


Другой вариант - игрок в стрелялке убивает противника или футбольная команда выигрывает матч и получает три очка.

zadd league_scores 1 team_1 0 team_2 0 team_3 1 team_4 // предыдущие результаты, у команд team_1 и team_4 было по одному очку

zincrby league_scores 3 team_2 // после сыгранного матча, вторая команда набирает три очка

zrange league_scores 0 -1 rev withscores // выводим в обратном порядке team_2 3 team_1 1 team_4 1 team_3 0


2) Реализуем очередь с приоритетами. Добавляем команды c разными приоритетами, затем поочередно получаем и удаляем команды из списка в порядке приоритета.

zadd commands 10 command_1 20 command_2 100 command_3 30 command_4

zpopmax commands 1 // вернет и удалит command_3 , в списке останется command_4 command_2 command_1


3) Реализуем ограничение по количеству запросов за период времени (rate limit), есть множество реализаций с различными свойствами, например эта.


4) Добавляем вторичные индексы для объектов. Например, добавляем несколько пользователей как отдельные хэши. Затем добавляем их возрасты в Sorted Set. После чего можем получить идентификаторы всех пользователей старше 18 лет.

hset 1 name alice age 15 ...

hset 2 name bob age 25 ...

hset 3 name charlie age 35 ...

zadd user_age 15 alice 25 bob 35 charlie // индексируем возраст

zrange user_age 18 +inf byscore // вернет bob charlie, остальную информацию о которых можно получить из хэшей.


На этом пока всё.



Report Page