Django多对多字段prefetch_related失效引发N+1查询优化问题
问题根因
你用prefetch_related预加载的是Carrera模型的实例对象,会缓存在estandarProducto.carreras的查询结果集中,但你调用.values()方法时,Django ORM 不会复用已缓存的实例,会重新向数据库发起查询,所以每遍历一个EstandarProducto实例就会多一次查询,导致总查询次数暴增。
修复方案
第一步:优化预加载查询
调整EstandarProducto的查询语句,预加载carreras时只取你需要的codigo字段,减少不必要的字段查询开销:
estandares = EstandarProducto.objects.select_related('recinto','producto','proveedor')\ .prefetch_related(Prefetch('carreras', queryset=Carrera.objects.only('codigo')))\ .filter(recinto=id_recinto)
这里only('codigo')表示预查询Carrera时只拉取id和codigo两个字段(id是关联必需字段,默认会查),比查询全表字段效率更高。
第二步:调整遍历逻辑,复用预加载缓存
把原遍历carreras的逻辑从调用.values()改为直接遍历预加载好的实例,这样就不会发起新的数据库查询:
# 原代码 # for carrera in estandarProducto.carreras.values(): # print("Codigo: " ,carrera.get("codigo")) # carreras.append(carrera.get("codigo")) # 替换为 for carrera in estandarProducto.carreras.all(): print("Codigo: ", carrera.codigo) carreras.append(carrera.codigo)
修改后carreras的所有数据都来自预加载的缓存,不会再产生额外的查询请求。
额外性能优化建议
你当前代码中嵌套遍历productos_en_inventario的逻辑时间复杂度是O(n*m),数据量变大后也会成为性能瓶颈,可以调整为字典映射的方式降低复杂度:
- 把原
productos_en_inventario = inventario.values()替换为:
# 以producto的id为key,预存库存数据,后续可以O(1)速度取值 producto_inventario_map = { item['id']: item for item in inventario.values('id', 'cantidad', 'ultimo_año_reposicion') }
- 把原遍历
productos_en_inventario的嵌套循环替换为直接取字典值:
# 原嵌套循环代码直接删除,替换为以下逻辑 inventario_item = producto_inventario_map.get(estandarProducto.producto.id, {}) cantidad_inventario = inventario_item.get('cantidad', 0) año_compra = inventario_item.get('ultimo_año_reposicion', '') id_inventario_producto = inventario_item.get('id', '')
内容的提问来源于stack exchange,提问作者Nuevakenia
相关产品推荐
相关产品推荐

