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

从Django 1.8迁移到3.2后数据库查询速度骤降问题

Django 1.8迁移到3.2后查询性能骤降问题排查与解决建议

问题背景

将项目从Django 1.8迁移到3.2后,相同数据库环境下,报表生成函数速度下降4倍(原耗时8秒,现耗时30-40秒)。数据库使用MySQL 5.7,更换为8.0.32版本后性能无改善。

核心查询代码

复杂查询:

qs = PartitaDanno.objects.filter(incarico__cliente_intestatario_fattura_id=8006
    ).select_related('incarico', 'anagrafica_assctp', 'incarico__naturaincarico'
    ).prefetch_related('rate'
    ).order_by('-incarico__anno', '-incarico__numero', 'pk')

简单查询(遍历仍需20秒):

qs = PartitaDanno.objects.filter(incarico__cliente_intestatario_fattura_id=8006)

关键数据

  • PartitaDanno表共16000行,模型包含215个字段
  • 查询仅返回1700行结果
  • 两个Django版本生成的原生SQL完全相同
  • 原生SQL在phpMyAdmin中执行仅需3.5秒
  • queryset.explain()结果显示SQL执行本身耗时较低,主要瓶颈在遍历查询集阶段

原生SQL与执行计划

原生SQL:

SELECT `sinistri_partitadanno`.`id`, ...(省略部分字段) ORDER BY `sinistri_incarico`.`anno` DESC, `sinistri_incarico`.`numero` DESC, `sinistri_partitadanno`.`id` ASC

执行计划:

-> Sort row IDs: sinistri_incarico.anno DESC, sinistri_incarico.numero DESC, sinistri_partitadanno.id  (actual time=163.955..172.010 rows=1761 loops=1)
-> Table scan on <temporary>  (cost=2518.91..2544.83 rows=1874) (actual time=153.040..163.135 rows=1761 loops=1)
    -> Temporary table  (cost=2518.90..2518.90 rows=1874) (actual time=153.007..153.007 rows=1761 loops=1)
        -> Nested loop left join  (cost=2331.46 rows=1874) (actual time=0.247..62.163 rows=1761 loops=1)
            -> Nested loop inner join  (cost=1675.43 rows=1874) (actual time=0.224..52.547 rows=1761 loops=1)
                -> Nested loop left join  (cost=1019.40 rows=1677) (actual time=0.089..17.068 rows=1677 loops=1)
                    -> Index lookup on sinistri_incarico using b842d1d4b6d5fa98d8dc06d2c92e02c5 (cliente_intestatario_fattura_id=8006)  (cost=432.45 rows=1677) (actual time=0.081..15.586 rows=1677 loops=1)
                    -> Single-row index lookup on sinistri_naturaincarico using PRIMARY (id=sinistri_incarico.naturaincarico_id)  (cost=0.25 rows=1) (actual time=0.001..0.001 rows=1 loops=1677)
                -> Index lookup on sinistri_partitadanno using sinistri_pa_incarico_id_398e55f0a3d8c2c0_fk_sinistri_incarico_id (incarico_id=sinistri_incarico.id)  (cost=0.28 rows=1) (actual time=0.018..0.021 rows=1 loops=1677)
            -> Single-row index lookup on T5 using PRIMARY (id=sinistri_partitadanno.anagrafica_assctp_id)  (cost=0.25 rows=1) (actual time=0.005..0.005 rows=1 loops=1761)

解决方案建议

  • 减少模型实例化开销:PartitaDanno有215个字段,Django 3.2实例化模型对象的开销远高于1.8。使用only()指定报表所需的字段,避免加载冗余字段:

    qs = PartitaDanno.objects.filter(incarico__cliente_intestatario_fattura_id=8006
        ).only('id', '所需字段1', '所需字段2'
        ).select_related('incarico', 'anagrafica_assctp', 'incarico__naturaincarico'
        ).prefetch_related('rate'
        ).order_by('-incarico__anno', '-incarico__numero', 'pk')
    

    也可使用defer()排除不需要的大字段或计算密集型字段。

  • 使用iterator()优化遍历:结合iterator()分批加载数据,避免一次性将所有实例存入内存,降低内存占用与遍历耗时。注意prefetch_related会在iterator()执行前完成预取,不会重复查询:

    for obj in qs.iterator():
        # 报表处理逻辑
    
  • 优化数据库连接配置:调整Django数据库连接参数,匹配MySQL的优化设置,例如:

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'your_db',
            'USER': 'your_user',
            'PASSWORD': 'your_pass',
            'HOST': 'your_host',
            'PORT': '3306',
            'OPTIONS': {
                'init_command': "SET sql_mode='STRICT_TRANS_TABLES', innodb_buffer_pool_size=256M",
                'charset': 'utf8mb4',
            },
            'CONN_MAX_AGE': 60,
        }
    }
    

    重点确保innodb_buffer_pool_size足够,提升数据库缓存效率。

  • 替换模型实例为字典/元组:如果报表生成不需要完整模型对象,使用values()或values_list()直接获取数据,跳过模型实例化步骤:

    qs = PartitaDanno.objects.filter(...).values('id', 'incarico__anno', 'incarico__numero')
    
  • 优化排序索引:创建联合索引sinistri_incarico(cliente_intestatario_fattura_id, anno DESC, numero DESC),让数据库直接返回排序后的结果,减少临时表排序的开销,间接降低Django后续处理的时间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 13:50:43