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

Django 1.11计算性能优化求助:5400行数据计算过慢

提速方案针对Django1.11+Python3.5+PostgreSQL场景

1. 消除N+1查询,用数据库聚合替代Python层面计算

当前循环内每次都拉取全量score数据到Python再做求和统计,IO开销极大。直接利用PostgreSQL聚合函数在数据库层面完成sum、count计算:

from django.db.models import Sum, Count

# 循环内替换原查询逻辑
current_year_agg = models.Answer.objects.filter(
    year_filter=year, **combination_filter
).aggregate(
    total_score=Sum('score'),
    answer_count=Count('id')
)
total_score = current_year_agg['total_score'] or 0
answer_count = current_year_agg['answer_count'] or 0
avg_score = int(total_score / answer_count) if answer_count > 0 else 0

history_agg = models.Answer.objects.filter(
    year_filter__lt=year, **combination_filter
).aggregate(
    total_history=Sum('score'),
    history_count=Count('id')
)
total_history = history_agg['total_history'] or 0
history_count = history_agg['history_count'] or 0
avg_history = int(total_history / history_count) if history_count > 0 else 0

每个组合仅需2次聚合查询,数据库直接返回计算结果,无需拉取全量score数据。

2. 批量更新数据库,减少请求次数

当前循环内每次调用update()都会发起一次数据库请求,5400条数据会产生5400次请求。改用bulk_update批量更新:

# 初始化列表存储待更新实例
update_list = []
# 定义要更新的字段列表
update_fields = [
    'score', 'history', 'country_benchmark', 'industry_benchmark',
    'hp_benchmark', 'dev0', 'dev1', 'dev2', 'dev3',
    's_respondent_count', 'h_respondent_count', 'cb_respondent_count',
    'ib_respondent_count', 'hp_respondent_count'
]

for combination in ees_combinations:
    # ... 中间计算逻辑保持不变 ...
    
    # 构造待更新实例
    combo_instance = models.Combination.objects.get(pk=combination['id'])
    for key, value in calculated_data.items():
        setattr(combo_instance, key, value)
    update_list.append(combo_instance)
    
    # 每100条批量更新一次,避免内存溢出
    if len(update_list) >= 100:
        models.Combination.objects.bulk_update(update_list, update_fields)
        update_list = []

# 处理剩余未更新的实例
if update_list:
    models.Combination.objects.bulk_update(update_list, update_fields)

Django1.11的bulk_update需要明确指定更新字段,这样能将数据库请求次数从5400次压缩到几十次。

3. 添加数据库索引,加速过滤查询

针对Answer表中频繁用于过滤的字段创建组合索引,大幅提升查询速度:

# 在Answer模型的Meta类中添加索引
class Answer(models.Model):
    # ... 现有字段定义 ...
    class Meta:
        indexes = [
            models.Index(
                fields=['year_filter', 'age_group', 'gender', 'division', 
                        'tenure', 'career_type', 'organization_filter', 'survey_filter']
            ),
        ]

也可针对单个高频过滤字段(如year_filter、organization_filter)添加单独索引,帮助PostgreSQL快速定位目标数据。

4. 异步执行计算任务,避免前端响应阻塞

将计算逻辑放到后台异步执行,前端先收到任务启动的响应,无需等待计算完成:

  1. 安装适配Python3.5和Django1.11的Celery版本(如Celery4.4.x),配合Redis/RabbitMQ作为消息队列
  2. 定义异步任务:
# tasks.py
from celery import shared_task
from django.db.models import Sum, Count
from . import models

@shared_task
def calculate_ees_data(survey, language, organization, year, answer, table):
    filters = {
        'organization_filter': organization,
        'survey_filter': survey,
        'language_filter': language,
        'year_filter': year,
        'answer_filter': answer,
        'table_filter': table
    }
    ees_combinations = models.Combination.objects.filter(**filters).values(
        'id', 'demographic', 'gender', 'division', 'tenure', 'career_type_level'
    ).iterator()
    
    update_list = []
    update_fields = [
        'score', 'history', 'country_benchmark', 'industry_benchmark',
        'hp_benchmark', 'dev0', 'dev1', 'dev2', 'dev3',
        's_respondent_count', 'h_respondent_count', 'cb_respondent_count',
        'ib_respondent_count', 'hp_respondent_count'
    ]
    
    for combination in ees_combinations:
        # ... 聚合计算逻辑同方案1 ...
        # ... 构造calculated_data ...
        
        combo_instance = models.Combination.objects.get(pk=combination['id'])
        for key, value in calculated_data.items():
            setattr(combo_instance, key, value)
        update_list.append(combo_instance)
        
        if len(update_list) >= 100:
            models.Combination.objects.bulk_update(update_list, update_fields)
            update_list = []
    
    if update_list:
        models.Combination.objects.bulk_update(update_list, update_fields)
  1. 在视图中调用异步任务:
def ees_calculate(request):
    if request.user.is_superuser:
        if request.is_ajax() and request.method == 'POST':
            # ... 获取POST参数 ...
            calculate_ees_data.delay(survey, language, organization, year, answer, table)
            return JsonResponse({'result':'success', 'message':'计算任务已后台启动'})
        # ... 其他权限校验逻辑 ...

5. 优化查询集,降低内存占用

将ees_combinations改为迭代器查询,避免一次性加载5400条数据到内存:

ees_combinations = models.Combination.objects.filter(**filters).values(
    'id', 'demographic', 'gender', 'division', 'tenure', 'career_type_level'
).iterator()

iterator()会逐个从数据库获取数据,大幅降低内存消耗。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:46:26