浏览知识库目录

Python

缓存、性能优化与异步任务

缓存、性能优化与异步任务

性能优化应从测量开始。本篇介绍数据库查询优化、缓存、异步 View 与后台任务,目标是知道瓶颈在哪里以及该用哪种工具。


一、先建立性能基线

优化前记录:

  • 接口平均、P95 和 P99 响应时间。
  • 每个请求的 SQL 数量与最慢 SQL。
  • 数据库 CPU、连接数和锁等待。
  • 缓存命中率。
  • 后台任务积压量和执行时长。
  • 错误率、超时率和吞吐量。

开发环境可使用 Django Debug Toolbar 查看 SQL,但不能安装在公开生产页面。


二、数据库查询优化

1. 消除 N+1

messages = (
    Message.objects
    .select_related("owner")
    .prefetch_related("tags", "comments")
)

2. 只读取需要的列

Message.objects.only("id", "name", "status")
Message.objects.values("id", "name")

only() 延迟加载其他字段,后续访问反而会产生额外查询。它不是默认优化手段,应在测量后使用。

3. 使用数据库聚合

from django.db.models import Count

Message.objects.annotate(comment_count=Count("comments"))

不要取出全部对象后在 Python 中做数据库更擅长的筛选、计数和聚合。

4. 合理索引

class Meta:
    indexes = [
        models.Index(fields=["status", "-created_at"]),
    ]

索引会加快读取,也会占空间并增加写入成本。根据真实查询条件和数据库执行计划设计。


三、缓存配置

本地开发可使用内存缓存:

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "notes-development",
    }
}

多进程生产环境通常使用 Redis 等共享缓存。安装并按所用后端文档配置连接、认证、TLS、超时和连接池。


四、缓存层级

1. 单视图缓存

from django.views.decorators.cache import cache_page


@cache_page(60 * 5)
def public_message_list(request):
    ...

2. 模板片段缓存

{% load cache %}
{% cache 300 popular_messages %}
  ...
{% endcache %}

3. 低级缓存 API

from django.core.cache import cache


def get_published_count():
    key = "messages:published-count:v1"
    value = cache.get(key)
    if value is None:
        value = Message.objects.filter(status="published").count()
        cache.set(key, value, timeout=300)
    return value

缓存键应包含影响结果的维度,例如用户、语言、权限、页码和筛选条件。不要把用户 A 的私有响应返回给用户 B。


五、缓存失效

缓存最难的问题通常不是写入,而是何时删除。常用策略:

  • 短 TTL:允许短时间旧数据,设计简单。
  • 写入后主动删除:数据更新时删除相关键。
  • 版本化键:更新版本号,让旧缓存自然过期。
  • 事件驱动失效:复杂系统通过任务或消息通知相关模块。
cache.delete("messages:published-count:v1")

缓存不是权威数据源。缓存不可用时,应用应尽可能回退到数据库,而不是直接丢失业务数据。


六、异步 View

from django.http import JsonResponse


async def health(request):
    return JsonResponse({"status": "ok"})

异步适合需要并发等待外部 I/O 的场景,不会自动让 CPU 密集计算或所有 ORM 操作变快。部署时需要 ASGI 服务器才能充分利用异步请求栈;同步中间件也可能削弱收益。

不要在异步 View 中直接调用阻塞型 HTTP 客户端或长时间计算。


七、后台任务

发送邮件、生成报表、图片处理和调用慢速第三方接口,不应让用户请求一直等待。常见方案是 Celery + Redis/RabbitMQ,也可以按项目规模选择其他任务队列。

概念示例:

# notes/tasks.py
from celery import shared_task
from django.core.mail import send_mail

from .models import Message


@shared_task(
    autoretry_for=(OSError,),
    retry_backoff=True,
    max_retries=5,
)
def send_publish_notification(message_id):
    message = Message.objects.select_related("owner").get(pk=message_id)
    send_mail(
        "留言已发布",
        message.name,
        "noreply@example.com",
        [message.owner.email],
    )

事务提交后再投递任务,避免 Worker 比事务更早读取数据:

from django.db import transaction

transaction.on_commit(
    lambda: send_publish_notification.delay(message.pk)
)

任务设计要点:

  • 尽量幂等,重复执行不会产生错误结果。
  • 传对象 ID,不传序列化后的完整模型实例。
  • 设置超时、重试上限和退避策略。
  • 区分可重试异常和永久失败。
  • 监控队列积压、失败任务和执行时长。

八、静态资源与网络层

  • 使用压缩、长期缓存和带内容哈希的静态资源。
  • 图片按展示尺寸生成缩略图,使用合适格式。
  • 大文件由对象存储/CDN 提供,不经 Django 进程转发。
  • 开启数据库持久连接前先评估进程数与连接上限。
  • 设置应用、数据库、缓存和第三方请求的超时。

九、本篇检查清单

  • 优化前能先记录性能指标。
  • 能识别 N+1 并选择关联加载方法。
  • 能选择页面、片段或低级缓存。
  • 能设计缓存键与失效策略。
  • 知道异步 View 与后台任务解决的是不同问题。

上一篇:使用 DRF 开发 RESTful API | 下一篇:Web 安全与生产配置