Настройка Redis для кэширования веб-приложения

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Настройка Redis для кэширования веб-приложения
Средняя
от 1 рабочего дня до 3 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Настройка Redis для кэширования веб-приложения

Redis — не просто кэш. Это структуры данных в памяти: строки, хэши, списки, множества, sorted sets, потоки, pub/sub. Правильно выбранная структура часто даёт более чистое решение, чем попытка сделать то же самое в реляционной базе.

Установка Redis 7.x

# Ubuntu 22.04+
apt install -y lsb-release curl gpg
curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" > /etc/apt/sources.list.d/redis.list
apt update && apt install -y redis

/etc/redis/redis.conf — ключевые параметры

# Сеть
bind 127.0.0.1 ::1
port 6379
requirepass your_strong_password_here

# Память
maxmemory 2gb
maxmemory-policy allkeys-lru    # вытеснять наименее используемые ключи
maxmemory-samples 10

# Персистентность для кэша — только AOF с минимальным fsync
save ""                          # отключить RDB снапшоты
appendonly yes
appendfsync everysec             # fsync каждую секунду
no-appendfsync-on-rewrite yes

# Производительность
tcp-keepalive 300
hz 20
lazyfree-lazy-eviction yes       # async-вытеснение
lazyfree-lazy-expire yes
io-threads 4                     # для Redis 6+ на многоядерных серверах
io-threads-do-reads yes

Паттерны кэширования

Cache-aside — самый распространённый:

// cache.service.ts
import { Redis } from 'ioredis'

export class CacheService {
  constructor(private redis: Redis) {}

  async getOrSet<T>(
    key: string,
    ttlSeconds: number,
    factory: () => Promise<T>
  ): Promise<T> {
    const cached = await this.redis.get(key)
    if (cached !== null) {
      return JSON.parse(cached) as T
    }
    const value = await factory()
    await this.redis.setex(key, ttlSeconds, JSON.stringify(value))
    return value
  }

  async invalidate(pattern: string): Promise<void> {
    // Используем SCAN, не KEYS — не блокирует Redis
    let cursor = '0'
    do {
      const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100)
      cursor = nextCursor
      if (keys.length > 0) {
        await this.redis.unlink(...keys)  // async удаление
      }
    } while (cursor !== '0')
  }
}

// Использование
const product = await cache.getOrSet(
  `product:${id}`,
  300,  // 5 минут
  () => db.products.findOne({ id })
)

// После обновления
await cache.invalidate(`product:${id}`)
await cache.invalidate(`products:category:${product.categoryId}:*`)

Write-through — запись в кэш и БД синхронно:

async updateProduct(id: string, data: UpdateProductDto) {
  const updated = await db.products.update(id, data)
  await redis.setex(`product:${id}`, 600, JSON.stringify(updated))
  return updated
}

Сессии пользователей

// session.service.ts
const SESSION_TTL = 86400 * 7  // 7 дней

export class SessionService {
  constructor(private redis: Redis) {}

  async create(userId: string, metadata: SessionMeta): Promise<string> {
    const sessionId = crypto.randomUUID()
    const key = `session:${sessionId}`

    await this.redis.hset(key, {
      userId,
      createdAt: Date.now(),
      ip: metadata.ip,
      userAgent: metadata.userAgent
    })
    await this.redis.expire(key, SESSION_TTL)

    // Добавить в список сессий пользователя
    await this.redis.zadd(`user:${userId}:sessions`, Date.now(), sessionId)
    await this.redis.expire(`user:${userId}:sessions`, SESSION_TTL)

    return sessionId
  }

  async get(sessionId: string): Promise<SessionData | null> {
    const data = await this.redis.hgetall(`session:${sessionId}`)
    if (!Object.keys(data).length) return null
    await this.redis.expire(`session:${sessionId}`, SESSION_TTL)  // sliding window
    return data as SessionData
  }

  async destroyAll(userId: string): Promise<void> {
    const sessionIds = await this.redis.zrange(`user:${userId}:sessions`, 0, -1)
    if (sessionIds.length) {
      const keys = sessionIds.map(id => `session:${id}`)
      await this.redis.unlink(...keys, `user:${userId}:sessions`)
    }
  }
}

Rate limiting через sliding window

async function rateLimit(
  redis: Redis,
  key: string,
  limit: number,
  windowMs: number
): Promise<{ allowed: boolean; remaining: number; resetAt: number }> {
  const now = Date.now()
  const windowStart = now - windowMs

  const pipeline = redis.pipeline()
  pipeline.zremrangebyscore(key, '-inf', windowStart)
  pipeline.zadd(key, now, `${now}-${Math.random()}`)
  pipeline.zcard(key)
  pipeline.pexpire(key, windowMs)

  const results = await pipeline.exec()
  const count = results![2][1] as number

  return {
    allowed: count <= limit,
    remaining: Math.max(0, limit - count),
    resetAt: now + windowMs
  }
}

Pub/Sub для событий

// publisher.ts
async function publishOrderEvent(event: OrderEvent) {
  await redis.publish('orders:events', JSON.stringify(event))
}

// subscriber.ts
const subscriber = redis.duplicate()
await subscriber.subscribe('orders:events')

subscriber.on('message', (channel, message) => {
  const event = JSON.parse(message) as OrderEvent
  eventBus.emit(event.type, event.payload)
})

Мониторинг

# Текущая нагрузка
redis-cli --latency-history -i 1
redis-cli info stats | grep -E "instantaneous_ops|rejected_connections|evicted_keys"

# Memory usage
redis-cli info memory | grep -E "used_memory_human|mem_fragmentation_ratio"

# Медленные команды
redis-cli config set slowlog-log-slower-than 10000  # 10ms
redis-cli slowlog get 10

Sentinel для высокой доступности

# sentinel.conf
sentinel monitor mymaster 10.0.0.1 6379 2
sentinel auth-pass mymaster your_password
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000
sentinel parallel-syncs mymaster 1

Сроки

Базовая установка Redis с настройкой кэширования и сессий для приложения: 1 день. Настройка Sentinel с автоматическим failover: 1–2 дня. Redis Cluster для горизонтального масштабирования с шардированием: 2–3 дня.