下载工作台
Django Web 开发

缓存与 Celery 异步入门

试读上半部分 · 解锁后可读全文

第 10 章 · 缓存与 Celery 异步入门

本章目标:使用 Redis 作为 Django 缓存后端;掌握视图缓存模板片段缓存低层 cache API;入门 Celery 分布式任务队列;用 @shared_task 实现异步发邮件与订单统计;在本地 Docker 完成 shop-demo 概念验证,理解生产架构分工。

学时建议:5 小时(含 2 小时 Docker + Celery 跟练)

前置:本模块 ch05 ORM 与查询;ch07 API;ch09 信号(异步副作用入口)。Redis 概念见 ops-deploy 相关章节。


10.1 为什么需要缓存与异步

问题同步方案痛点本 chapter 方案
商品列表重复查库每次请求打满 MySQLRedis 页面/查询缓存
注册后发欢迎邮件用户等 SMTP 3~5 秒Celery 后台发信
日订单汇总报表管理页超时定时任务 @shared_task
                    ┌─────────────┐
  HTTP 请求 ───────►│ Django      │
                    │  views      │
                    └──────┬──────┘
                           │ cache.get/set
                           ▼
                    ┌─────────────┐
                    │ Redis       │◄──── Celery Worker
                    └─────────────┘      读/写缓存、发邮件
                           ▲
                    ┌──────┴──────┐
                    │ MySQL       │
                    └─────────────┘
shop-demo 为教学项目;Redis 地址示例 redis://127.0.0.1:6379/1,邮件使用控制台 backend,不连接真实业务中间件

10.2 本地 Redis 与 docker-compose

shop-demo/docker-compose.dev.yml

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

volumes:
  redis_data:
docker compose -f docker-compose.dev.yml up -d redis
redis-cli ping   # PONG

10.3 Django 缓存配置

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",
        },
        "KEY_PREFIX": "shopdemo",
        "TIMEOUT": 300,  # 默认 5 分钟
    }
}

验证

# python manage.py shell
from django.core.cache import cache
cache.set("ping", "pong", 60)
cache.get("ping")  # 'pong'
方法说明
cache.set(key, value, timeout)写入
cache.get(key, default=None)读取
cache.delete(key)删除
cache.get_or_set(key, callable, timeout)读或计算后写

10.4 缓存商品列表查询

# catalog/services.py
from django.core.cache import cache
from .models import Product

PRODUCT_LIST_KEY = "product_list:published:v1"


def get_published_products():
    data = cache.get(PRODUCT_LIST_KEY)
    if data is not None:
        return data
    qs = list(
        Product.objects.filter(is_published=True)
        .select_related("category")
        .values("id", "name", "price", "slug")[:50]
    )
    cache.set(PRODUCT_LIST_KEY, qs, timeout=120)
    return qs

失效策略(与 ch09 信号结合):

from django.core.cache import cache
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from catalog.models import Product
from catalog.services import PRODUCT_LIST_KEY


@receiver([post_save, post_delete], sender=Product)
def invalidate_product_list_cache(sender, **kwargs):
    cache.delete(PRODUCT_LIST_KEY)

原则:先更新库,再删缓存(cache-aside)。


10.5 页面缓存

全页缓存适合变化少、个性化低的页面(如帮助页、公开商品分类树)。

from django.views.decorators.cache import cache_page
from django.utils.decorators import method_decorator


@cache_page(60 * 5)  # 5 分钟
def category_tree(request):
    ...


# CBV
@method_decorator(cache_page(60 * 5), name="dispatch")
class PublicProductListView(ListView):
    model = Product

注意

  • 含用户个性化内容的页面不要整页缓存
  • API(DRF)一般用查询缓存而非 cache_page
  • 使用 @vary_on_cookie 可按 Cookie 区分缓存键

10.6 模板片段缓存

{% load cache %}
{% cache 300 product_sidebar category.id %}
  <aside>
    {% for p in hot_products %}
      <li>{{ p.name }}</li>
    {% endfor %}
  </aside>
{% endcache %}

300 为秒数;缓存键自动包含片段内容与 category.id


10.7 Celery 安装与配置

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。