一、现象描述
1.表结构
class Instance(BaseModel):
check_object = models.ForeignKey(CheckObject, on_delete=models.CASCADE, db_constraint=False)
xxx
class InstanceAttr(BaseModel):
instance = models.ForeignKey(Instance, on_delete=models.CASCADE, db_constraint=False)
xxx
2.现象
在查询数据时,Instance
有七千多条数据,instanceattr
数据量36w+,单纯地循环,都要7s多。
inst_obj = Instance.objects.select_related("check_object").prefetch_related("instanceattr_set").filter(
check_object__bk_object_key__in=check_show_fields.keys()).distinct().all()
start_time = time.time()
for inst in inst_obj:
pass
end_time = time.time()
print('Took %f second' % (end_time - start_time))
输出结果:
Took 7.414227 second
二、原因分析
经过分析,发现是使用prefetch_related
导致的。
从下图的返回结果中可以看出,在循环过程中,select_related
和prefetch_related
都会调用一次数据库。但是由于prefetch_related
对应的sql条件多,数据量大(36w+),查询耗时2s,导致整个函数都变慢。
从中我们可以看出,并不是什么时候,都适合使用prefetch_related
来优化代码,要结合实际使用。
三、优化方法
- 不使用
prefetch_related
,而是在循环之前,直接查询InstanceAttr
的数据,然后映射成字典,在循环中get
数据。 - 在查询
InstanceAttr
的数据时,添加更多的附加条件,减少数据量。
最终优化结构如下,循环从7s下降到0.2s
Took 0.232967 second