Ở tập 8, chúng ta đã gắn endpoint /health và metrics để biết hệ thống còn sống hay không. Tập 9 trả lời câu hỏi tiếp theo: làm sao để hệ thống đáp ứng nhanh hơn khi lượng truy cập tăng. Với Redis làm cache, ứng dụng quản lý chi tiêu có thể trả về danh sách giao dịch và báo cáo tháng trong vài mili-giây thay vì truy vấn PostgreSQL mỗi lần — nhưng chỉ khi cache đúng chỗ và xóa đúng lúc.

1. Cài đặt Redis và cấu hình cache

Cài gói django-redis rồi khai báo cache mặc định trỏ tới Redis. Giữ Redis chạy cùng máy hoặc một container riêng, đặt mật khẩu và giới hạn bộ nhớ bằng maxmemory-policy allkeys-lru để Redis tự loại key ít dùng nhất khi đầy.

# settings/production.py
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": env("REDIS_URL"), # redis://127.0.0.1:6379/1
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
"KEY_PREFIX": "expense",
"TIMEOUT": 300, # 5 phút mặc định
}
}

2. Cache danh sách giao dịch theo từng người dùng

Quy tắc đầu tiên: key cache phải chứa id người dùng, vì dữ liệu chi tiêu là riêng tư. Dùng cache.get, chỉ truy vấn database khi cache trống, và luôn đặt timeout để dữ liệu cũ tự hết hạn.

# expenses/views.py
from django.core.cache import cache
def monthly_summary(request, year, month):
key = f"summary:{request.user.id}:{year}-{month:02d}"
data = cache.get(key)
if data is None:
data = compute_summary(request.user, year, month)
cache.set(key, data, timeout=300)
return JsonResponse(data)

3. Xóa cache đúng lúc: invalidation bằng signal

Cache chỉ an toàn khi dữ liệu thay đổi thì cache cũ biến mất ngay. Gắn signal post_save và post_delete của model Transaction để xóa key báo cáo của đúng tháng và đúng người dùng đó, thay vì xóa toàn bộ cache. Cách này giữ tỷ lệ cache hit cao mà không trả số liệu cũ.

# expenses/signals.py
from django.core.cache import cache
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from .models import Transaction
@receiver([post_save, post_delete], sender=Transaction)
def invalidate_summary(sender, instance, **kwargs):
key = f"summary:{instance.user_id}:{instance.date:%Y-%m}"
cache.delete(key)

4. Những lỗi cache hay gặp

  • Cache chung một key cho mọi người dùng: người này thấy số liệu người khác — luôn gắn user id vào key.
  • Không đặt timeout: key lỗi nằm lì trong cache tới khi ai đó xóa tay — luôn đặt TIMEOUT mặc định.
  • Cache trang chứa CSRF token hoặc dữ liệu nhạy cảm: chỉ cache fragment không nhạy cảm, hoặc bỏ qua cache.
  • Xóa cache bằng cache.clear() mỗi lần deploy: Redis trống rỗng, database chịu toàn bộ tải — đổi KEY_PREFIX theo phiên bản deploy thay vì xóa hết.

Áp dụng vào dự án của bạn

Bắt đầu từ một endpoint duy nhất: trang báo cáo tháng của ứng dụng quản lý chi tiêu. Đo thời gian đáp ứng trước và sau khi bật cache bằng /metrics đã gắn ở tập 8, rồi mới mở rộng sang danh sách giao dịch và trang tổng quan. Nếu sau một tuần tỷ lệ cache hit dưới 50%, xem lại timeout và kiểm tra signal xóa cache có chạy đúng không.

Bài viết liên quan

Câu hỏi thường gặp

Khi nào nên dùng cache, khi nào không?

Cache dữ liệu đọc nhiều, đổi ít: báo cáo tháng, danh sách danh mục. Không cache dữ liệu vừa ghi xong cần thấy ngay, số dư sau mỗi giao dịch, hay trang chứa thông tin nhạy cảm theo phiên làm việc.

Redis mất dữ liệu khi restart thì sao?

Cache được thiết kế để mất được: khi cache trống, code truy vấn lại database và nạp lại cache. Chỉ cần luôn có đường fallback khi cache.get trả về None, hệ thống vẫn chạy đúng, chỉ chậm hơn một lúc.

Đặt timeout bao lâu là hợp lý?

Bắt đầu với 5 phút cho báo cáo và 60 giây cho danh sách đổi thường xuyên, rồi điều chỉnh theo tỷ lệ cache hit. Timeout ngắn an toàn hơn timeout dài, vì dữ liệu cũ tự biến mất.

Cache stampede là gì và tránh thế nào?

Là khi một key hot hết hạn, hàng trăm request cùng truy vấn database một lúc. Cách đơn giản: dùng cache.get_or_set với khóa, hoặc đặt timeout lệch nhau để các key không hết hạn cùng lúc.

Nguồn tham khảo

  • Tài liệu Django: Caching framework — docs.djangoproject.com/en/stable/topics/cache/
  • django-redis — github.com/jazzband/django-redis