Ответ
Реализация кеширования — это компромисс между скоростью, актуальностью данных и сложностью. В моих проектах я применял многоуровневый подход.
1. In-memory кеш приложения (Local Cache): Используется для данных, специфичных для экземпляра приложения или очень горячих. Например, справочники конфигурации.
-
Реализация на Python (используя
functools.lru_cache):from functools import lru_cache import requests @lru_cache(maxsize=128, ttl=300) # Кеш на 128 элементов с TTL 5 минут def get_country_name(country_code: str) -> str: # Дорогой вызов к внешнему API или БД response = requests.get(f'https://api.example.com/countries/{country_code}') return response.json()['name']
2. Распределенный кеш (Distributed Cache): Критически важен для масштабируемых приложений с несколькими инстансами. Redis — стандартный выбор.
-
Паттерн Cache-Aside (Lazy Loading):
import redis import json redis_client = redis.Redis(host='redis-cluster.example.com', port=6379, decode_responses=True) def get_user_profile(user_id: int): cache_key = f'user_profile:{user_id}' # 1. Проверяем кеш cached_data = redis_client.get(cache_key) if cached_data: return json.loads(cached_data) # 2. Если нет в кеше, идем в БД (например, PostgreSQL) user_data = db_session.query(User).filter_by(id=user_id).first() if not user_data: return None # 3. Сохраняем в кеш на 1 час redis_client.setex(cache_key, 3600, json.dumps(user_data.to_dict())) return user_data -
Инвалидация кеша: При обновлении данных в БД необходимо удалять или обновлять соответствующий ключ в Redis.
def update_user_profile(user_id: int, new_data): # 1. Обновляем основное хранилище db_session.query(User).filter_by(id=user_id).update(new_data) db_session.commit() # 2. Инвалидируем кеш cache_key = f'user_profile:{user_id}' redis_client.delete(cache_key) # Или сразу обновляем кеш новыми данными # redis_client.setex(cache_key, 3600, json.dumps(new_data))
3. HTTP-кеширование: Для статического контента или редко меняющихся API-ответов.
-
Использование заголовков:
from flask import make_response @app.route('/api/catalog') def get_catalog(): data = get_catalog_data() # Может быть закешировано внутри response = make_response(json.dumps(data)) response.headers['Cache-Control'] = 'public, max-age=300' # 5 минут в кеше браузера/CDN response.headers['ETag'] = generate_etag(data) # Для валидации return response
Выбор стратегии:
- Cache-Aside (Lazy Loading): Универсален, но возможен cache miss penalty.
- Write-Through: Данные записываются одновременно в кеш и БД. Сложнее, но обеспечивает согласованность.
- Write-Behind (Write-Back): Запись идет сначала в кеш, а потом асинхронно в БД. Максимальная скорость записи, но риск потери данных при сбое.
В продакшене мы комбинировали Redis для данных сессий и горячих справочников, а для тяжелых агрегаций использовали CDN или предварительно рассчитанные материализованные представления в БД.