Django Caching mit Redis: Strategien für schnelle Anwendungen
Inhalt
Caching ist einer der wirkungsvollsten Hebel für die Performance einer Django-Anwendung. Mit Redis als In-Memory-Store bekommst du Antwortzeiten im Millisekunden-Bereich. Ich setze Redis-Caching in fast jedem meiner Projekte ein.
Redis als Cache-Backend einrichten
pip install 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",
"COMPRESSOR": "django_redis.compressors.zlib.ZlibCompressor",
},
"TIMEOUT": 300, # 5 Minuten Standard-TTL
"KEY_PREFIX": "myapp",
}
}Low-Level Cache API
from django.core.cache import cache
# Setzen
cache.set("my_key", {"data": 42}, timeout=600) # 10 Min
# Lesen
value = cache.get("my_key")
if value is None:
value = expensive_computation()
cache.set("my_key", value, timeout=600)
# get_or_set: Atomisch (empfohlen!)
value = cache.get_or_set("my_key", expensive_computation, timeout=600)
# Mehrere Keys auf einmal
cache.set_many({"key1": v1, "key2": v2}, timeout=300)
values = cache.get_many(["key1", "key2"])
# Loeschen
cache.delete("my_key")
cache.delete_many(["key1", "key2"])
cache.clear() # Alles loeschen (Vorsicht!)View- und Template-Caching
from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator
# Function-Based View
@cache_page(60 * 15) # 15 Minuten
def product_list(request):
...
# Class-Based View
@method_decorator(cache_page(60 * 15))
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
# Per-User cachen (cache_control)
from django.views.decorators.vary import vary_on_headers
@cache_page(300)
@vary_on_headers("Accept-Language", "Cookie")
def my_view(request):
...Query-Ergebnisse cachen
from django.core.cache import cache
def get_active_products(category_id):
cache_key = f"active_products_{category_id}"
products = cache.get(cache_key)
if products is None:
products = list(
Product.objects
.filter(category_id=category_id, active=True)
.select_related("category")
.values("id", "name", "price") # values() ist JSON-serialisierbar
)
cache.set(cache_key, products, timeout=3600)
return productsWichtig: Nur serialisierbare Daten cachen. Django-Model-Instanzen direkt zu cachen funktioniert, kann aber zu veralteten Daten führen. Besser:.values()oder.values_list()nutzen.
Cache-Invalidierung
Cache-Invalidierung ist das schwierigste Problem im Caching. Meine bewährte Strategie: Signal-basierte Invalidierung:
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.cache import cache
@receiver([post_save, post_delete], sender=Product)
def invalidate_product_cache(sender, instance, **kwargs):
# Alle betroffenen Cache-Keys loeschen
cache.delete(f"active_products_{instance.category_id}")
cache.delete(f"product_detail_{instance.id}")
cache.delete("product_count") # Globaler Count
# Oder mit Pattern-Delete (django-redis Feature):
from django_redis import get_redis_connection
con = get_redis_connection("default")
con.delete_pattern("*active_products_*")Tipps aus der Praxis
- Key-Präfixe strukturieren:
"app:model:id:field"macht Muster-Löschung einfach - TTL nicht vergessen: Kein Cache sollte ewig leben, außer bei expliziter Invalidierung
- Cache-Stampede verhindern: Bei großem Traffic können alle gleichzeitig den Cache neu befüllen — Jitter (zufällige TTL-Variation) hilft
- Nie sensible Daten cachen: Passwörter, Tokens, personenbezogene Daten gehören nicht in den Cache
