Какие существуют способы реализации кэширования в Python

«Какие существуют способы реализации кэширования в Python» — вопрос из категории Архитектура, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

1. Внутрипроцессное кэширование (In-memory)

Кэш хранится в оперативной памяти текущего процесса. Идеально подходит для ускорения повторных вызовов "тяжелых" функций с одинаковыми аргументами (мемоизация).

Решение: декоратор functools.lru_cache.

LRU (Least Recently Used) означает, что при заполнении кэша вытесняются самые давно неиспользуемые элементы.

import time
from functools import lru_cache

@lru_cache(maxsize=128)
def fetch_data(n: int) -> int:
    """Имитация долгой операции, например, запроса к БД."""
    print(f"Выполняется сложный расчет для {n}...")
    time.sleep(1)
    return n * n

print(fetch_data(5)) # Расчет займет 1 секунду
print(fetch_data(5)) # Результат вернется мгновенно из кэша
print(fetch_data(10))# Расчет займет 1 секунду
print(fetch_data(10))# Результат вернется мгновенно из кэша
  • Преимущества: Очень быстро, легко реализовать, входит в стандартную библиотеку.
  • Недостатки: Кэш живет только в рамках одного процесса и сбрасывается при его перезапуске. Аргументы функции должны быть хешируемыми.

2. Распределенное кэширование (Distributed)

Кэш хранится во внешнем, независимом сервисе (например, Redis или Memcached). Используется в веб-приложениях и распределенных системах.

Задачи:

  • Снижение нагрузки на базу данных.
  • Обмен данными между разными процессами или серверами.
  • Хранение сессий пользователей.

Пример с Redis:

import redis
import json

# Подключение к серверу Redis
r = redis.Redis(decode_responses=True)

def get_user_profile(user_id: int):
    cache_key = f"user:{user_id}"

    # 1. Проверяем наличие данных в кэше
    cached_data = r.get(cache_key)
    if cached_data:
        print(f"Данные для user:{user_id} взяты из кэша Redis")
        return json.loads(cached_data)

    # 2. Если в кэше нет, получаем данные из БД (имитация)
    print(f"Данные для user:{user_id} запрошены из БД")
    db_data = {"id": user_id, "name": "Alice", "email": "alice@example.com"}

    # 3. Сохраняем данные в кэш с временем жизни (TTL) 60 секунд
    r.set(cache_key, json.dumps(db_data), ex=60)

    return db_data

get_user_profile(123) # Запрос к БД
get_user_profile(123) # Данные из кэша
  • Преимущества: Доступен из любого процесса/сервера, устойчив к перезапускам приложений, гибкие настройки времени жизни (TTL).
  • Недостатки: Требует развертывания и поддержки отдельного сервиса, более медленный доступ по сравнению с in-memory кэшем из-за сетевых задержек.