> Какие типы и структуры данных поддерживает Redis (Python)

Уровень: senior · Роль: backend · Язык: Python · Категория: Технические вопросы

Компании: MTS

Стек: Redis, Python

> Пример ответа

Короткий ответ

Redis поддерживает богатый набор структур данных: строки, списки, множества, хеши, сортированные множества, а также более сложные типы - битовые карты, гиперлоги, геопространственные индексы, потоки (streams) и модули вроде JSON, Bloom filter и Time Series. Каждый тип оптимизирован под конкретные сценарии: кэширование, очереди, рейтинги, аналитику, геолокацию. В Python доступ к ним осуществляется через клиенты redis-py, которые транслируют типы в Python-объекты.

Подробное объяснение

Redis - это in-memory key-value store, но ключевое отличие от простых хранилищ - встроенные структуры данных, каждая из которых имеет свои операции и сложность. Основные типы:

  • String - базовый тип, хранит строки, числа, бинарные данные. Поддерживает атомарные операции: INCR, DECR, APPEND, GETRANGE. Используется для кэшей, счётчиков, сессий.
  • List - упорядоченный список строк, реализован как linked list. Операции: LPUSH, RPUSH, LPOP, RPOP, LRANGE. Применяется для очередей задач, лент новостей, стека.
  • Set - неупорядоченное множество уникальных строк. Операции: SADD, SREM, SISMEMBER, SUNION, SINTER. Подходит для тегов, фильтрации, подсчёта уникальных элементов.
  • Hash - map из полей в значения, эффективен для хранения объектов. Операции: HSET, HGET, HGETALL, HINCRBY. Используется для профилей пользователей, настроек, корзин.
  • Sorted Set (ZSet) - множество с оценкой (score), элементы упорядочены по score. Операции: ZADD, ZRANGE, ZRANK, ZINCRBY. Применяется для рейтингов, лидербордов, таймеров, очередей с приоритетом.
  • Bitmap - по сути строка, но операции работают на уровне битов: SETBIT, GETBIT, BITCOUNT, BITOP. Используется для трекинга посещений, фиче-флагов.
  • HyperLogLog - вероятностная структура для подсчёта уникальных элементов с точностью ~0.81%. Операции: PFADD, PFCOUNT, PFMERGE. Применяется для уникальных посетителей, подсчёта событий.
  • Geo - обёртка над ZSet для хранения координат. Операции: GEOADD, GEOSEARCH, GEODIST. Используется для поиска ближайших объектов.
  • Stream - append-only лог с consumer groups, похож на Kafka. Операции: XADD, XREAD, XGROUP, XACK. Применяется для event sourcing, очередей сообщений, метрик в реальном времени.
  • Модули - расширения, например RedisJSON (JSON-документы), RedisBloom (Bloom filter, Cuckoo filter), RedisTimeSeries (time-series данные). Требуют установки модуля на сервере.

Внутренняя реализация: строки могут быть int, embstr или raw; списки - linked list или quicklist; хеши - listpack или hashtable; ZSet - skiplist + hash table. Выбор зависит от размера данных и паттернов доступа.

На практике

Выбор структуры данных - это ключевое архитектурное решение. Например, для кэша профиля лучше использовать Hash, а не сериализованный JSON в строке - так можно обновлять отдельные поля без чтения всего объекта. Для очереди задач - List с BLPOP для блокирующего чтения. Для лидерборда - ZSet, где score - очки, а member - ID пользователя.

В Python с redis-py типы транслируются автоматически: строки - bytes, хеши - dict, списки - list, множества - set. Важно помнить, что все значения в Redis - байтовые строки, поэтому числа нужно конвертировать явно.

Пример типичного сценария - rate limiter на основе INCR с TTL, или дедупликация через SETNX. Для аналитики - HyperLogLog вместо Set, если точность не критична, но важна память.

Пример кода

PYTHON
import redis
import time
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# String: счётчик
r.set('visits', 0)
r.incr('visits')
print(r.get('visits')) # '1'
# Hash: профиль пользователя
r.hset('user:1001', mapping={'name': 'Alice', 'age': 30})
r.hincrby('user:1001', 'age', 1)
print(r.hgetall('user:1001')) # {'name': 'Alice', 'age': '31'}
# List: очередь задач
r.rpush('tasks', 'task1', 'task2')
task = r.blpop('tasks', timeout=5) # ('tasks', 'task1')
# ZSet: лидерборд
r.zadd('leaderboard', {'player1': 100, 'player2': 150})
r.zincrby('leaderboard', 10, 'player1')
top = r.zrevrange('leaderboard', 0, 1, withscores=True)
print(top) # [('player2', 150.0), ('player1', 110.0)]
# HyperLogLog: уникальные посетители
r.pfadd('unique_visitors', 'ip1', 'ip2', 'ip1')
print(r.pfcount('unique_visitors')) # 2
# Stream: событие
event_id = r.xadd('events', {'type': 'click', 'user': 'alice'})
print(event_id) # '1712345678900-0'

Как отвечать на собеседовании

Начни с перечисления основных типов, затем кратко опиши назначение каждого. Подчеркни, что выбор структуры зависит от сценария и сложности операций. Упомяни, что Redis - не просто key-value, а платформа для структур данных. Если спросят про внутреннее устройство - скажи про listpack, skiplist, quicklist, но не углубляйся, если не просят. Покажи понимание trade-off: например, Set vs HyperLogLog - точность против памяти. Обязательно приведи пример из практики, где ты выбирал тип под задачу. Если вопрос про Python - упомяни redis-py и особенности декодирования.

Что проверяет интервьюер

  • Знание полного набора структур данных, а не только строк и хешей.
  • Понимание, когда какой тип использовать, и почему.
  • Осведомлённость о сложности операций (O(1), O(log N)).
  • Практический опыт: как структуры применяются в реальных системах.
  • Глубину: знание внутренней реализации и модулей.

Типичные ошибки

  • Путать List и Set, не понимать разницу между упорядоченностью и уникальностью.
  • Использовать String для хранения объектов, когда лучше Hash.
  • Не знать про HyperLogLog и Stream - это часто спрашивают на senior.
  • Забывать, что все значения - байтовые, и в Python нужно декодировать.
  • Предлагать Set для подсчёта уникальных элементов при больших объёмах - лучше HyperLogLog.
  • Не упоминать про TTL и expiration, хотя это важная часть работы с типами.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью