> Какой опыт работы с Redis, его структурами данных, pub/sub и стримами (Node.js, Redis, JavaScript, Python)

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

Компании: ЭНИРАН

Стек: Node.js, Redis, JavaScript, Python

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

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

У меня более 5 лет опыта работы с Redis в production-среде. Активно использовал строки, хеши, списки, sets, sorted sets для кэширования, очередей задач и рейтингов. Pub/sub применял для real-time уведомлений, а стримы - для надежной обработки событий с consumer groups. В Node.js использовал ioredis, в Python - redis-py. Настраивал кластеризацию, персистентность RDB/AOF и мониторинг через RedisInsight.

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

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

  • Strings - для кэширования сессий, rate limiting (INCR, EXPIRE)
  • Hashes - для хранения объектов пользователей, настроек (HSET, HGETALL)
  • Lists - для очередей задач (LPUSH/BRPOP), логов
  • Sets - для уникальных идентификаторов, тегов (SADD, SISMEMBER)
  • Sorted Sets - для лидербордов, очередей с приоритетом (ZADD, ZRANGEBYSCORE)

Pub/sub использовал в архитектуре event-driven: сервис публикует события, подписчики получают их в реальном времени. Однако pub/sub не гарантирует доставку - если подписчик отключен, сообщение теряется. Для критичных сценариев применял стримы.

Redis Streams - более надежная альтернатива pub/sub. Использовал XADD для записи, XREADGROUP для consumer groups с авто-балансировкой нагрузки. Это позволило реализовать exactly-once processing с помощью XACK и PEL (pending entries list). В production настраивал maxlen для ограничения размера стрима.

На практике

В одном проекте мы заменили RabbitMQ на Redis Streams для обработки заказов. Consumer groups обеспечили fault-tolerance: при падении одного consumer’а, его pending entries перераспределялись между другими. Для мониторинга использовали XLEN и XINFO GROUPS.

Для кэширования применял паттерн cache-aside с TTL. При обновлении данных инвалидировал кэш через DEL или устанавливал новое значение. Для high-load эндпоинтов использовал pipeline (ioredis.pipeline()) для batch-операций, что снизило RTT на 70%.

В rate limiting использовал Lua-скрипты для атомарного INCR + EXPIRE. Это исключило race conditions при параллельных запросах.

Пример кода

JAVASCRIPT
// Node.js: Redis Streams consumer group
const Redis = require('ioredis');
const redis = new Redis();
async function processOrders() {
const groupName = 'order-processors';
const streamName = 'orders';
// Создаем consumer group (если не существует)
try {
await redis.xgroup('CREATE', streamName, groupName, '$', 'MKSTREAM');
} catch (e) {
// Group already exists
}
while (true) {
const results = await redis.xreadgroup(
'GROUP', groupName, 'worker-1',
'BLOCK', 2000,
'COUNT', 10,
'STREAMS', streamName, '>'
);
if (results) {
for (const [, messages] of results) {
for (const [id, fields] of messages) {
const order = JSON.parse(fields[1]);
await processOrder(order);
await redis.xack(streamName, groupName, id);
}
}
}
}
}
PYTHON
# Python: cache-aside with Redis
import redis
import json
r = redis.Redis(host='localhost', decode_responses=True)
def get_user(user_id: int) -> dict:
cache_key = f"user:{user_id}"
# Try cache first
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# Miss - load from DB
user = db.query(User).filter_by(id=user_id).first()
if user:
user_dict = user.to_dict()
r.setex(cache_key, 3600, json.dumps(user_dict))
return user_dict
return None

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

Начни с конкретных примеров использования Redis в production, а не с теории. Упомяни trade-offs: почему выбрал Redis Streams вместо Kafka или RabbitMQ. Покажи понимание внутреннего устройства - как работают skiplist в sorted sets, как Redis обеспечивает single-threaded event loop для атомарности.

Опиши проблемы, с которыми сталкивался: memory fragmentation, latency spikes при BGSAVE, network overhead при больших payloads. Расскажи, как решал - через tuning maxmemory-policy, использование lazy freeing, настройку TCP backlog.

Для senior-позиции важно показать архитектурное мышление: как проектировал схемы данных, выбирал между разными структурами, балансировал между consistency и performance.

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

  • Глубину понимания Redis: не только синтаксис команд, но и внутреннее устройство (event loop, persistence, replication)
  • Умение выбирать правильную структуру данных под задачу
  • Опыт работы с отказоустойчивостью: sentinel, cluster, replication
  • Понимание trade-offs: pub/sub vs streams, RDB vs AOF, single instance vs cluster
  • Навыки оптимизации: pipeline, Lua scripting, batch operations

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

  • Использование pub/sub для критичных данных без понимания потери сообщений
  • Неправильный выбор структуры: хранение JSON в строках вместо хешей
  • Игнорирование memory management: отсутствие TTL, maxmemory-policy
  • Слепое копирование схем из реляционных БД в Redis
  • Отсутствие мониторинга: не следят за used_memory, connected_clients, rejected_connections
  • Неправильная настройка persistence: AOF с always sync убивает производительность
  • Использование KEYS в production вместо SCAN

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

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