You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django返回Response时CPU占用过高问题求助

问题分析与解决方案

根据你描述的现象和提供的代码,CPU耗时集中在MyJsonResponse打印"done"之后,说明问题出在响应对象创建完成后,Django处理响应的后续流程或之前查询的结果处理延迟暴露,以下是具体原因排查和解决方向:


一、核心原因推测

  1. Django响应阶段的中间件开销
    打印"done"后,Django会通过中间件链处理响应(process_response方法)。如果你的项目启用了非必要中间件(如调试类、日志类、缓存类中间件),这些组件可能在响应阶段执行大量计算(比如debug toolbar收集全量请求数据、缓存中间件生成复杂缓存键、日志中间件序列化响应内容),导致CPU占用飙升。

  2. WSGI服务器的响应处理逻辑
    无论是开发服务器runserver还是gunicorn,都会对HttpResponse对象进行迭代传输。如果你的自定义MyJsonResponse存在隐藏的迭代开销(比如未正确处理字节流转换,导致WSGI服务器反复解析内容),会额外消耗CPU。

  3. 查询结果的延迟处理(容易被误解为响应阶段耗时)
    Django的QuerySet是惰性执行的,你在view中调用list()时才会真正执行查询,但如果patents是包含大量数据的QuerySet(比如id__in的列表过大),8次annotate查询的结果转换(从数据库对象到Python字典)可能会延迟到list()调用的后期,让你误以为是打印"done"后的耗时。


二、具体解决步骤

1. 替换自定义MyJsonResponse为Django原生JsonResponse

你的自定义类手动调用json.dumps后传入HttpResponse,可能触发Django内部的重复编码逻辑。改用原生JsonResponse,它会使用Django优化后的序列化流程,避免不必要的CPU消耗:

# 替换原return语句
return JsonResponse(
    {
        "applications_per_year": {
            "": group_fields(applications_per_year, ["application_year"])
        },
        "granted_patents_per_year": {
            "": group_fields(granted_patents_per_year, ["granted_year"])
        },
        # ... 其余数据结构保持不变
    }
)

2. 禁用非必要中间件排查

临时注释settings.MIDDLEWARE中除核心组件外的所有中间件(如DebugToolbarMiddleware、自定义日志中间件等),测试CPU占用是否下降:

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    # 注释掉其他非必要中间件
]

3. 优化数据库查询性能

从debug toolbar数据看,8次__iter__调用耗时17.5秒,这是CPU消耗的重要来源:

  • 给分组字段加索引:在Patent模型的application_year、granted_year、type、office字段添加db_index=True,加速GROUP BY查询:
    class Patent(models.Model):
        application_year = models.IntegerField(db_index=True)
        granted_year = models.IntegerField(db_index=True)
        type = models.CharField(max_length=20, db_index=True)
        office = models.CharField(max_length=20, db_index=True)
        # ... 其他字段
    
  • 减少查询次数:将多个独立的annotate查询合并,比如使用Subquery或自定义SQL一次性获取所有聚合结果,避免重复扫描数据集。
  • 优化id__in查询:如果patent_ids是超大列表(万级以上),改用PostgreSQL的ANY操作符或临时表JOIN替代IN子句,提升查询效率:
    # PostgreSQL下优化id__in查询
    from django.db.models import Func, F
    patents = Patent.objects.filter(id=Func(F(patent_ids), function='ANY'))
    

4. 用性能分析工具定位响应阶段耗时

使用cProfile或django-silk工具,精准定位响应阶段的CPU消耗点:

  • 用cProfile分析整个请求流程:
    python -m cProfile -o profile_stats.py manage.py runserver
    # 用snakeviz可视化分析结果
    snakeviz profile_stats.py
    
  • 安装django-silk,它会自动记录中间件、视图、响应等各个阶段的耗时,帮助你快速定位瓶颈。

内容的提问来源于stack exchange,提问作者Constantinos Petrakis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 17:45:54