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

Django使用prefetch_related解决N+1查询问题的实操咨询

N+1查询问题原理与优化方案

原代码的问题

你当前的写法触发了典型的N+1查询问题,总查询次数等于ProductSpy的数量加1:

  • 第1次查询:ProductSpy.objects.all() 执行1次SQL,拉取所有ProductSpy的基础数据
  • 第N次查询:循环中每次访问productSpy.product时,Django会为每个ProductSpy单独发起1次SQL查询对应的Product数据,若有N条ProductSpy记录就会产生N次额外查询,数据量越大性能损耗越严重。

关联查询优化逻辑说明

Django提供了两种提前加载关联数据的方案:

  • select_related:通过SQL JOIN的方式,将关联表的字段直接拼接在主表的查询结果中,仅执行1次SQL,仅支持正向外键、一对一关联场景
  • prefetch_related:分两次查询,第一次拉取主表数据,第二次通过IN语句一次性拉取所有关联的外键数据,再在Python层完成数据关联,支持正向/反向外键、多对多关联场景

你的场景属于ProductSpy正向关联Product的外键场景,两种方案都可以使用,优先选select_related性能更好。

优化代码

方案1:使用select_related(推荐)

# Get the amount of kilo attached to products
product_data = {}
# 提前加载关联的product对象,全程仅执行1次SQL
for productSpy in ProductSpy.objects.select_related('product').all():
    product_data[productSpy.product.product_id] = productSpy.kilo
# Get the amount of kilo attached to products
product_data = {}
# 提前预取关联的product对象,全程执行2次SQL,避免N+1问题
for productSpy in ProductSpy.objects.prefetch_related('product').all():
    product_data[productSpy.product.product_id] = productSpy.kilo

方案3:最高性能写法(无需实例化ORM对象)

如果仅需要product_id和kilo两个字段,可直接通过values_list查询所需字段,省略ORM对象实例化的开销,仅执行1次SQL:

product_data = dict(ProductSpy.objects.values_list('product__product_id', 'kilo'))

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 03:15:07