Django Cache 설정 및 활용 가이드
Django의 캐시 프레임워크를 설정하고 다양한 캐싱 전략을 활용하는 방법을 알아봅니다.
Django Cache 설정 및 활용 가이드
Django는 다양한 캐시 백엔드를 지원하며, 적절한 캐싱 전략을 사용하면 애플리케이션 성능을 크게 향상시킬 수 있습니다.
Environment
$ python --version
Python 3.11.5
$ pip show django
Name: Django
Version: 4.2.5
$ pip show django-redis
Name: django-redis
Version: 5.4.0Problem: 반복적인 DB 쿼리로 인한 성능 저하
# views.py - 매 요청마다 DB 쿼리 실행
def product_list(request):
# These queries run every time
products = Product.objects.filter(is_active=True)
categories = Category.objects.all()
featured = Product.objects.filter(featured=True)[:5]
return render(request, 'products/list.html', {
'products': products,
'categories': categories,
'featured': featured,
})실행 결과:
# Each request executes 3+ queries
[SQL] SELECT * FROM products WHERE is_active = True
[SQL] SELECT * FROM categories
[SQL] SELECT * FROM products WHERE featured = True LIMIT 5Analysis: Django 캐시 프레임워크 이해
# settings.py - 캐시 설정 구조
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
# Available backends:
# - locmem: Local memory (development)
# - memcached: Memcached server
# - redis: Redis server
# - file: File-based cache
# - database: Database cacheSolution: 다양한 캐시 설정 방법
기본 LocMem 캐시 설정
# settings.py
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'product-cache',
'TIMEOUT': 300, # 5 minutes default
'OPTIONS': {
'MAX_ENTRIES': 1000,
}
}
}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',
'SERIALIZER': 'django_redis.serializers.json.JSONSerializer',
'CONNECTION_POOL_KWARGS': {'max_connections': 100},
'SOCKET_CONNECT_TIMEOUT': 5,
'SOCKET_TIMEOUT': 5,
},
'KEY_PREFIX': 'myproject',
'TIMEOUT': 300,
}
}뷰 레벨 캐싱
from django.views.decorators.cache import cache_page
from django.views.decorators.vary import vary_on_cookie
@cache_page(60 * 15) # Cache for 15 minutes
@vary_on_cookie # Cache per user
def product_list(request):
products = Product.objects.filter(is_active=True)
categories = Category.objects.all()
return render(request, 'products/list.html', {
'products': products,
'categories': categories,
})Low-level Cache API
from django.core.cache import cache
def get_products():
# Try to get from cache
products = cache.get('active_products')
if products is None:
# Cache miss - query database
products = list(Product.objects.filter(is_active=True))
# Store in cache for 10 minutes
cache.set('active_products', products, timeout=600)
return products
# Cache with version
cache.set('products', products, timeout=600, version=1)
# Get with default value
products = cache.get('products', default=[])
# Delete specific cache
cache.delete('active_products')
# Clear all cache
cache.clear()Template Fragment Caching
{% load cache %}
{# Cache this fragment for 30 minutes #}
{% cache 300 product_list %}
{% for product in products %}
{{ product.name }}
{% endfor %}
{% endcache %}
{# Cache per user #}
{% cache 300 user_profile request.user.id %}
{{ user.profile }}
{% endcache %}Low-Level Cache with Stampede Prevention
from django.core.cache import cache
import time
def get_or_set(key, callback, timeout=300):
"""Get from cache or set with callback, preventing cache stampede"""
value = cache.get(key)
if value is None:
# Try to acquire lock
lock_key = f"lock_{key}"
if cache.add(lock_key, True, timeout=10):
try:
# We got the lock, compute value
value = callback()
cache.set(key, value, timeout=timeout)
finally:
cache.delete(lock_key)
else:
# Another process is computing, wait and retry
time.sleep(0.1)
return cache.get(key)
return value
# Usage
products = get_or_set(
'active_products',
lambda: list(Product.objects.filter(is_active=True)),
timeout=600
)Lessons Learned
캐시 백엔드 선택: 개발 환경은 LocMem, 프로덕션은 Redis나 Memcached를 사용하세요.
캐시 키 관리: 캐시 키는 명확하고 구조화된 이름을 사용하여 충돌을 방지하세요.
캐시 무효화: 데이터가 변경될 때 관련 캐시를 명시적으로 삭제하는 것을 잊지 마세요.
캐시 스탬피드: 여러 프로세스가 동시에 캐시 만료 시 DB를 요청하는 현상을 방지하세요.
캐시 히트율 모니터링: Django Debug Toolbar로 캐시 히트율을 모니터링하여 효과를 확인하세요.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.