Site icon PythonRu

Redis для приложений на Python

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

Почему Redis?

Обычно при упоминании Redis у многих возникают ассоциации с базами данных NoSQL, но это в корне неверно. У Redis нет с ними ничего общего: ни в плане своего позиционирования, ни в плане исполнения. MongoDB, например, хранит данные на диске.

Выделение места для записей подразумевает, что эти данные должны быть сохранены: аккаунты пользователей, записи в блог, разрешения и так далее. Большая часть данных любых приложений относится к этой категории.

Тем не менее есть и исключения. Было бы крайне неэффективно хранить, например, содержимое корзины пользователя или информацию о последней посещенной странице. В краткосрочной перспективе такая информация была бы полезной, но нагружать ею базы данных, основанные на транзакционных системах — не очень разумно. Благо, существует такое понятие как RAM (ОЗУ или оперативное запоминающее устройство).

Redis — это резидентная база данных (такая, которая хранит записи прямо в оперативной памяти) в виде пар ключ-значение. Чтение и запись в память происходит намного быстрее, чем в случае с дисками, поэтому такой подход отлично подходит для хранения второстепенных данных.

Это улучшает пользовательский опыт, но одновременно делает базы данных чистыми. Если же в будущем решается, что такие данные тоже нужно хранить, то их всегда можно записать на диск (например, в базу данных SQL).

В этом руководстве познакомимся с библиотекой Python для Redis под названием redis-py. В среде Python его называют просто redis. Официальная документация этой библиотеки — просто одна страница с перечислением всех методов в алфавитном порядке.

Если вы планируете использовать Redis с каким-либо из фреймворков, то рекомендуется выбирать конкретную библиотеку: например, Flask-Redis, а не redis-py. Однако все они преимущественно повторяют синтаксис redis-py и имеют несколько минимальных отличий.

Установка Redis

Что бы протестировать работу Redis рекомендую использовать облачное решение. Зарегистрируйтесь на Redis Labs, они дают бесплатный сервер для обучения и тестирования.

  1. Пройдите регистрацию.
  2. Подтвердите почту.
  3. Создайте подписку (сервер).

4. Создайте базу данных:

После активации приложения вам понадобятся хост(Endpoint) и пароль (Default User Password).

Далее установим redis:

pip install redis

Строка подключения к Redis

Как и в случае с обычными базами данных подключить экземпляр Redis можно с помощью строки подключения. Вот как такая выглядит в Redis:

redis://:hostname.redislabs.com@mypassword:12345/0

Разберем по пунктам:

[CONNECTION_METHOD]:[HOSTNAME]@[PASSWORD]:[PORT]/[DATABASE]

Создание клиента Redis

URI есть. Теперь нужно подключиться к Redis, создав объект клиента:


import redis

r = redis.StrictRedis(
host='redis-17449.c55.eu-central-1-1.ec2.cloud.redislabs.com', # из Endpoint
port=17449, # из Endpoint
password='qwerty' # ваш пароль
)

Но почему StrictRedis, вы можете спросить? Есть два вида создания клиентов Redis: redis.Redis() и redis.StrictRedis(). StrictRedis пытается правильно применять типы данных. Старые экземпляры так не умеют. redis.Redis() — обратно совместимая с устаревшими экземплярами Redis версия с любыми наборами данных, а redis.StrictRedis() — нет. Если сомневаетесь — используйте StrictRedis.

Есть множество других аргументов, которые можно (и нужно) передать в redis.StrictRedis(), чтобы упростить себе жизнь. Обязательно передайте decode_responses=True, ведь это избавит необходимости явно расшифровывать каждое значение из базы. Также не лишним будет указать кодировку:


import redis

r = redis.StrictRedis(
host='redis-17449.c55.eu-central-1-1.ec2.cloud.redislabs.com',
port=17449,
password='qwerty',
charset="utf-8",
decode_responses=True
)

Пример использования Redis

Хранилище ключ-значение Redis очень напоминает словари Python, отсюда и расшифровка — Remote Dictionary Service (удаленный сервис словарей). Ключи — это всегда строки, но значениями могут быть разные типы данных. Заполним приложение Redis несколькими записями, чтобы лучше понять, как они работают:


import redis
import time

r = redis.StrictRedis(
host='redis-17449.c55.eu-central-1-1.ec2.cloud.redislabs.com',
port=17449,
password='qwerty',
charset="utf-8",
decode_responses=True
)

r.set('ip_address', '127.0.0.0')
r.set('timestamp', int(time.time()))
r.set('user_agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 11)')
r.set('last_page_visited', 'home')

r.set([KEY], [VALUE]) — это основной синтаксис, чтобы задавать одиночные значения. Первый параметр — это ключ, а второй — присваиваемое ему значение.

По аналогии с обычными базами данных подключение к Redis осуществляется с помощью графического интерфейса, по типу TablePlus для проверки данных. Вот как выглядит тестовая база после выполнения кода выше:

KEYVALUETYPETTL
user_agentMozilla/5.0 (Macintosh; Intel Mac OS X 11)STRING-1
last_page_visitedhomeSTRING-1
ip_address127.0.0.0STRING-1
timestamp1610803181STRING-1

Кажется, операция прошла успешно. Узнать кое-что о Redis можно, просто взглянув на таблицу. Начнем с колонки type.

Типы данных в Redis

В Redis могут храниться данные 5 типов:

Срок хранения данных

В базе данных Redis есть четвертая колонка под названием ttl. В нашем примере для всех записей в ней было значение -1. Когда же там положительное значение, то оно указывает на количество секунд до истечения срока действия данных. Redis — отличное хранилище для временно полезных данных, однако не таких, которые нужны в долгосрочной перспективе. Вот когда полезно устанавливать срок действия — это позволяет не нагружать приложение информацией, которая быстро становится нерелевантной.

Вернемся к примеру, где хранится информация о сессии пользователя и зададим срок действия данных:


...

r.set('last_page_visited', 'home', 86400)

В этот раз передадим третье значение в r.set(). Оно указывает на количество секунд, которое должно пройти до истечения срока действия данных. Снова проверим базу данных:

KEYVALUETYPETTL
user_agentMozilla/5.0 (Macintosh; Intel Mac OS X 11)STRING-1
last_page_visitedhomeSTRING86400
ip_address127.0.0.0STRING-1
timestamp1610803181STRING-1

Работа с каждым типом данных

Теория — это прекрасно, но мы знаем, зачем вы здесь: чтобы воспользоваться кодом, который можно будет применить в своем приложении. Рассмотрим несколько примеров распространенного использования 5 типов данных в Redis.

Строки

Если строки включают целые числа, то есть несколько методов, с помощью которых можно их изменять так, будто бы это целые числа. Это incr(), decr() и incrby():


# Создать строковое значение
r.set('index', '1')
print(f"index: {r.get('index')}")

# Увеличить строку на 1
r.incr('index')
print(f"index: {r.get('index')}")

# Уменьшить строку на 1
r.decr('index')
print(f"index: {r.get('index')}")

# Увеличить строку на 3
r.incrby('index', 3)
print(f"index: {r.get('index')}")

Это присвоит значение ‘1’ ключу index, увеличит его на 1, уменьшит на 1 и в итоге увеличит еще на 3:

index: 1
index: 2
index: 1
index: 4

Списки

Добавим элементы в список Redis с помощью комбинации .lpush() и rpush(), а также удалим их оттуда с помощью .lpop(). Классика:


r.lpush('my_list', 'A')
print(f"my_list: {r.lrange('my_list', 0, -1)}")

# Добавить вторую строку в список справа
r.rpush('my_list', 'B')
print(f"my_list: {r.lrange('my_list', 0, -1)}")

# Вставить третью строку в список справа
r.rpush('my_list', 'C')
print(f"my_list: {r.lrange('my_list', 0, -1)}")

# Удалить из списка 1 экземпляр, значение которого "C"
r.lrem('my_list', 1, 'C')
print(f"my_list: {r.lrange('my_list', 0, -1)}")

# Вставить строку в наш список слева
r.lpush('my_list', 'C')
print(f"my_list: {r.lrange('my_list', 0, -1)}")

# Вытащить первый элемент нашего списка и переместить его в конец
r.rpush('my_list', r.lpop('my_list'))
print(f"my_list: {r.lrange('my_list', 0, -1)}")

Вот как будет выглядеть список на разных этапах:


my_list: ['A']
my_list: ['A', 'B']
my_list: ['A', 'B', 'C']
my_list: ['A', 'B']
my_list: ['C', 'A', 'B']
my_list: ['A', 'B', 'C']

Множества

Множества являются мощным инструментом отчасти благодаря их способности взаимодействовать между собой. Дальше создаются два отдельных множества и на них применяются операции .sunion() и .sinter():


# Добавить элемент в set 1
r.sadd('my_set_1', 'Y')
print(f"my_set_1: {r.smembers('my_set_1')}") # Добавить элемент в set 1
r.sadd('my_set_1', 'X')
print(f"my_set_1: {r.smembers('my_set_1')}") # Добавить элемент в set 2
r.sadd('my_set_2', 'X')
print(f"my_set_2: {r.smembers('my_set_2')}") # Добавить элемент в set 2
r.sadd('my_set_2', 'Z')
print(f"my_set2: {r.smembers('my_set_2')}") # Объединение set 1 и set 2
print(f"sunion: {r.sunion('my_set_1', 'my_set_2')}") # Пересечение set 1 и set 2
print(f"sinter: {r.sinter('my_set_1', 'my_set_2')}")

Первая объединяет наборы без повторов, а вторая — выбирает общие элементы:

my_set_1: {'Y'}
my_set_1: {'X', 'Y'}
my_set_2: {'X'}
my_set2: {'X', 'Z'}
sunion: {'X', 'Y', 'Z'}
sinter: {'X'}

Сортированные множества

Добавление элементов в сортированное множество с помощью .zadd() предполагает интересный синтаксис. Обратите внимание на то, что для добавления записей требуется словарь в формате {[VALUE]: [INDEX]}:


# Создали отсортированный set с 3 значениями
r.zadd('top_songs_set', {'Never Change - Jay Z': 1,
'Rich Girl - Hall & Oats': 2,
'The Prayer - Griz': 3})
print(f"top_songs_set: {r.zrange('top_songs_set', 0, -1)}")

# Добавили элемент в set с конфликтующим значением
r.zadd('top_songs_set', {"Can't Figure it Out - Bishop Lamont": 3})
print(f"top_songs_set: {r.zrange('top_songs_set', 0, -1)}")

# Индекс сдвига значения
r.zincrby('top_songs_set', 3, 'Never Change - Jay Z')
print(f"top_songs_set: {r.zrange('top_songs_set', 0, -1)}")

У элементов в сортированном множестве никогда не может быть одного и того же индекса, так что при попытке добавить элемент на место существующего индекса, текущий элемент (и все после него) сдвигаются, чтобы дать место новому. Также есть возможность менять индексы после их создания:

top_songs_set: ['Never Change - Jay Z', 'Rich Girl - Hall & Oats', 'The Prayer - Griz']
top_songs_set: ['Never Change - Jay Z', 'Rich Girl - Hall & Oats', "Can't Figure it Out - Bishop Lamont", 'The Prayer - Griz']'
top_songs_set: ['Rich Girl - Hall & Oats', "Can't Figure it Out - Bishop Lamont", 'The Prayer - Griz', 'Never Change - Jay Z']

Хэши

Это просто добавление и получение данных из хэшей:


record = {
"name": "PythonRu",
"description": "Redis tutorials",
"website": "https://pythonru.com/"
}
r.hmset('business', record)
print(f"business: {r.hgetall('business')}")

Вывод такой же, как и ввод:

business: {'name': 'PythonRu', 'description': 'Redis tutorials', 'website': 'https://pythonru.com/'}

Exit mobile version