JavaRush /Курсы /Модуль 4: FastAPI /Как уменьшить нагрузку на серверы с помощью кэширования

Как уменьшить нагрузку на серверы с помощью кэширования

Модуль 4: FastAPI
9 уровень , 7 лекция
Открыта

Теперь настало время применить всё, что мы изучили, и разобраться, как именно кэширование может уменьшить нагрузку на серверы. Мы погрузимся в реальные сценарии оптимизации, чтобы ваши приложения могли работать быстро и устойчиво даже при высокой нагрузке.

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

Вообразите, что у вас есть API, который обрабатывает тысячи запросов в минуту. Например, вы ведёте базу данных всех кофейных точек в городе (где лучший капучино, разумеется). Каждый раз, когда пользователь отправляет запрос, ваше приложение ходит в базу данных, чтобы достать нужную информацию. При этом:

  • Запросы становятся медленнее. Обращения к базе данных требуют времени, особенно при сложных запросах или больших объёмах данных.
  • Сервер начинает задыхаться. Чем больше запросов обрабатывается, тем больше ресурсов требуется (CPU, RAM).
  • Возрастает вероятность отказа. Под высокими нагрузками сервер может перестать отвечать.

Зачем тут Redis?

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


Использование кэша для снижения нагрузки

Теперь давайте разберем, как именно Redis помогает снизить нагрузку на серверы. Начнем с простого примера и постепенно усложним его.

Допустим, вы разрабатываете API для поиска кофейных точек. Вот ваш эндпоинт:


@app.get("/coffee-shops")
async def get_coffee_shops(location: str):
    # 1. Выполняем тяжёлый запрос к базе данных
    coffee_shops = database.query(f"SELECT * FROM coffee_shops WHERE location='{location}'")
    return {"data": coffee_shops}

Ваш сервер заходит в базу данных каждый раз, когда кто-то запрашивает кофейни определённой локации. Это медленно. Давайте закэшируем результат:


import aioredis
from fastapi import FastAPI

app = FastAPI()
redis = await aioredis.from_url("redis://localhost")

@app.get("/coffee-shops")
async def get_coffee_shops(location: str):
    # 1. Проверяем, есть ли данные в кэше
    cached_shops = await redis.get(location)
    if cached_shops:
        return {"data": cached_shops}

    # 2. Если данных нет, выполняем тяжелый запрос к базе данных
    coffee_shops = database.query(f"SELECT * FROM coffee_shops WHERE location='{location}'")

    # 3. Сохраняем результат в Redis
    await redis.set(location, coffee_shops, ex=3600)  # Устанавливаем TTL на 1 час

    return {"data": coffee_shops}

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


Уменьшение нагрузки на базу данных

Представьте, что запросы к вашему API обслуживают 10 000 пользователей. С Redis большая часть этих запросов обрабатывается прямо из кэша. Это означает:

  • Меньше запросов к базе данных.
  • Меньшее использование памяти и процессорного времени сервера базы данных.
  • Ускорение времени отклика для пользователей.

На практике, вы можете сокращать до 90% нагрузки на базу данных (да, звучит как магия, но это реальность).


Какие данные лучше кэшировать?

Кэшировать имеет смысл данные, которые:

  1. Часто запрашиваются.
  2. Меняются редко (или изменения можно легко отслеживать).

Например:

  • Популярные товары в интернет-магазине.
  • Географические данные (например, кофейни в определённой локации).
  • Ответы API с высокой частотой запросов.

Выбор времени жизни (TTL)

Не забывайте про TTL (Time to Live). Это время, в течение которого данные будут храниться в кэше. Если данные устаревают, их нужно освежить. TTL помогает автоматически удалять старые данные.

Пример с TTL:


await redis.set(location, coffee_shops, ex=3600)  # 1 час

Инвалидирование кэша

Иногда данные в базе меняются. Например, новая кофейня открылась. Чтобы пользователи не получали устаревшую информацию, вам нужно инвалидировать кэш — удалить или обновить старые данные.

Пример:


# При добавлении новой кофейни
@app.post("/coffee-shops")
async def add_coffee_shop(new_shop: dict):
    # Сохраняем в базу данных
    database.insert("coffee_shops", new_shop)

    # Инвалидируем кэш для локации
    location = new_shop["location"]
    await redis.delete(location)

Оптимизация нагрузки с помощью кэширования

Теперь давайте рассмотрим более сложный пример, где мы используем кэш для уменьшения нагрузки сразу на несколько компонентов системы.

Представьте, что ваше приложение работает с внешним API, чтобы получать погодные данные. Внешний API медленный и дорогой (вы платите за каждый запрос). Давайте закэшируем ответы.

Пример:


import requests

@app.get("/weather")
async def get_weather(location: str):
    # Сначала проверяем кэш
    cached_weather = await redis.get(f"weather:{location}")
    if cached_weather:
        return {"data": cached_weather}

    # Если кэша нет, запрашиваем данные из внешнего API
    response = requests.get(f"https://weather-api.com/{location}")
    weather_data = response.json()

    # Сохраняем данные в кэш
    await redis.set(f"weather:{location}", weather_data, ex=600)  # 10 минут

    return {"data": weather_data}

Теперь вместо того, чтобы каждый раз обращаться к внешнему API, сервер сохраняет данные в Redis. Это снижает нагрузку на ваше приложение и экономит деньги.


Анализ результатов

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

Инструменты мониторинга Redis:

  • Redis Monitoring
  • Использование встроенной команды MONITOR.

Советы и лучшие практики

  • Не пытайтесь кэшировать всё. Кэширование данных с высокой изменчивостью может быть дороже самой проблемы.
  • Используйте уникальные ключи для кэша. Например, f"user:{user_id}:profile".
  • Настраивайте TTL в зависимости от типа данных.
  • Подумайте про автоматические механизмы инвалидирования кэша (например, при изменении данных).
  • Мониторьте производительность Redis, чтобы он сам не стал узким местом.

Теперь вы вооружены знаниями, как использовать кэширование для уменьшения нагрузки на серверы. В следующей лекции мы продолжим исследовать другие способы оптимизации и заглянем ещё глубже в возможности Redis!

1
Задача
Модуль 4: FastAPI, 9 уровень, 7 лекция
Недоступна
Кэширование данных по ключу
Кэширование данных по ключу
1
Задача
Модуль 4: FastAPI, 9 уровень, 7 лекция
Недоступна
Реализация кэширования для API-эндпоинта
Реализация кэширования для API-эндпоинта
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ