← Назад к вопросам

Как реализовать кэширование?

2.0 Middle🔥 161 комментариев
#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как реализовать кэширование

Кэширование — это техника хранения часто используемых данных в быстром хранилище для ускорения последующих запросов.

1. Python встроенное кэширование

@lru_cache (Least Recently Used):

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_computation(n):
    print(f"Computing {n}")
    return n ** 2

print(expensive_computation(5))  # Computing 5 → 25
print(expensive_computation(5))  # Из кэша → 25
print(expensive_computation(10)) # Computing 10 → 100

@cache (без размера):

from functools import cache

@cache
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

2. Redis — рекомендуемый кэш

Установка:

pip install redis

Базовое использование:

import redis
import json

# Подключение
redis_client = redis.Redis(host='localhost', port=6379, db=0)

# Сохранение
redis_client.set('user:123', json.dumps({'name': 'John', 'age': 30}), ex=3600)  # 1 час

# Получение
value = redis_client.get('user:123')
if value:
    user = json.loads(value)
    print(user)  # {'name': 'John', 'age': 30}

Паттерн cache-aside:

def get_user(user_id):
    # 1. Проверить кэш
    cached = redis_client.get(f'user:{user_id}')
    if cached:
        return json.loads(cached)
    
    # 2. Если нет в кэше, получить из БД
    user = db.query(User).filter(User.id == user_id).first()
    
    # 3. Сохранить в кэш
    redis_client.set(f'user:{user_id}', json.dumps(user.dict()), ex=3600)
    
    return user

3. Django кэширование

Настройка Redis:

# settings.py
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

Использование в Django:

from django.core.cache import cache
from django.views.decorators.cache import cache_page

# Декоратор для вьюх (кэш на 5 минут)
@cache_page(60 * 5)
def user_list(request):
    users = User.objects.all()
    return render(request, 'users.html', {'users': users})

# Ручное кэширование
def get_user(user_id):
    cache_key = f'user:{user_id}'
    user = cache.get(cache_key)
    
    if user is None:
        user = User.objects.get(id=user_id)
        cache.set(cache_key, user, timeout=3600)  # 1 час
    
    return user

4. FastAPI кэширование

С Redis:

from fastapi import FastAPI
from fastapi_cache2 import FastAPICache2
from fastapi_cache2.backends.redis import RedisBackend
from fastapi_cache2.decorators import cache
from redis import asyncio as aioredis

app = FastAPI()

@app.on_event("startup")
async def startup():
    redis = aioredis.from_url("redis://localhost")
    FastAPICache2.init(RedisBackend(redis), prefix="fastapi-cache")

@app.get("/users/{user_id}")
@cache(expire=3600)
async def get_user(user_id: int):
    # Кэшируется автоматически
    user = await db.get_user(user_id)
    return user

5. Memcached

Установка:

pip install pymemcache

Использование:

from pymemcache.client.base import Client

client = Client(('localhost', 11211))

# Установка
client.set('key', b'value', expire=3600)

# Получение
value = client.get('key')
if value:
    print(value.decode())  # value

6. In-Memory кэш

Простой вариант:

from datetime import datetime, timedelta

class SimpleCache:
    def __init__(self):
        self.store = {}
    
    def get(self, key):
        if key in self.store:
            value, expires_at = self.store[key]
            if expires_at > datetime.now():
                return value
            else:
                del self.store[key]  # Удалить истёкший
        return None
    
    def set(self, key, value, expire_seconds=3600):
        expires_at = datetime.now() + timedelta(seconds=expire_seconds)
        self.store[key] = (value, expires_at)

cache = SimpleCache()
cache.set('user:123', {'name': 'John'}, expire_seconds=3600)
print(cache.get('user:123'))  # {'name': 'John'}

7. Декоратор кэширования

from functools import wraps
from datetime import datetime, timedelta

def cached(expire_seconds=3600):
    def decorator(func):
        cache = {}
        
        @wraps(func)
        def wrapper(*args, **kwargs):
            key = (args, tuple(kwargs.items()))
            
            if key in cache:
                value, expires_at = cache[key]
                if expires_at > datetime.now():
                    return value
                else:
                    del cache[key]
            
            result = func(*args, **kwargs)
            cache[key] = (result, datetime.now() + timedelta(seconds=expire_seconds))
            return result
        
        return wrapper
    return decorator

@cached(expire_seconds=300)
def expensive_function(x):
    print(f"Computing {x}")
    return x ** 2

print(expensive_function(5))  # Computing 5 → 25
print(expensive_function(5))  # Из кэша → 25

8. Query кэширование

from django.db.models import Q

# Кэширование результата запроса
def get_active_users():
    cache_key = 'active_users'
    users = cache.get(cache_key)
    
    if users is None:
        users = User.objects.filter(is_active=True)
        cache.set(cache_key, list(users), timeout=3600)
    
    return users

# Инвалидация кэша при обновлении
def update_user(user_id, name):
    user = User.objects.get(id=user_id)
    user.name = name
    user.save()
    
    # Инвалидировать кэш
    cache.delete('active_users')
    cache.delete(f'user:{user_id}')

9. HTTP кэширование

from fastapi import FastAPI
from fastapi.responses import Response

app = FastAPI()

@app.get("/data")
async def get_data(response: Response):
    # Кэшировать на клиенте
    response.headers["Cache-Control"] = "public, max-age=3600"
    response.headers["ETag"] = '"123456"'
    
    return {"data": "value"}

10. Cache Layers

# Многоуровневое кэширование
def get_user_optimized(user_id):
    # L1: локальный кэш (быстро)
    user = local_cache.get(f'user:{user_id}')
    if user:
        return user
    
    # L2: Redis (среднее)
    user = redis.get(f'user:{user_id}')
    if user:
        local_cache.set(f'user:{user_id}', user, expire=60)
        return user
    
    # L3: Database (медленно)
    user = db.get_user(user_id)
    redis.set(f'user:{user_id}', user, expire=3600)
    local_cache.set(f'user:{user_id}', user, expire=60)
    
    return user

Стратегии инвалидации

# 1. Time-based (TTL)
cache.set('key', value, expire=3600)  # Автоматически удалится через час

# 2. Event-based
def on_user_updated(user_id):
    cache.delete(f'user:{user_id}')
    cache.delete('all_users')

# 3. LRU
@lru_cache(maxsize=128)  # Удаляет самые старые при превышении
def func():
    pass

# 4. Manual
cache.delete('specific_key')
cache.clear()  # Очистить все

Вывод: Кэширование критично для production приложений. Выбор инструмента зависит от needs:

  • Функции: @lru_cache
  • Веб-приложение: Redis
  • Простой прототип: In-memory кэш
  • Распределённая система: Redis/Memcached