Python
缓存、性能优化与异步任务
发布于 2026年7月22日
缓存、性能优化与异步任务
性能优化应从测量开始。本篇介绍数据库查询优化、缓存、异步 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 安全与生产配置