浅谈Django查询优化select_related和prefetch_related

421 阅读2分钟

一、select_related查询优化

select_related通过多表jong关联查询,它将“跟随”外键关系,在执行查询时选择额外的相关对象数据,导致一个更复杂的单一查询,但意味着以后使用外键关系将不需要数据库查询。

下面的例子说明了普通查找和 select_related()  查找之间的区别。下面是标准的查询:

# Hits the database.
e = Entry.objects.get(id=5)

# Hits the database again to get the related Blog object.
b = e.blog

这里是 select_related 查找:

# Hits the database.
e = Entry.objects.select_related('blog').get(id=5)

# Doesn't hit the database, because e.blog has been prepopulated
# in the previous query.
b = e.blog

二、prefetch_related查询优化

与 select_related 有类似的目的,二者都是为了阻止因访问相关对象而引起的数据库查询,减少SQL查询对数量,但策略却完全不同。

select_related 的工作方式是创建一个 SQL 连接,并在 SELECT 语句中包含相关对象的字段。通过join语句,在SQL查询内解决问题,但是仅限于单值关系——外键和一对一。对于多对多关系,使用SQL语句,会导致join得到的表将会很长,使SQL语句运行时间增加和内存占用增加

prefetch_related 则对每个关系进行单独的查找,并在 Python 中进行“joining”。这使得它除了支持 select_related 的外键和一对一关系外,还可以预取多对多和多对一的对象,这是用 select_related 无法做到的。它还支持 GenericRelation 和 GenericForeignKey 的预取。

总结

1、select_related是通过join来关联多表,一次获取数据,存放在内存中,但如果关联的表太多,会严重影响数据库性能。

2、prefetch_related是通过分表,先获取各个表的数据,存放在内存中,然后通过Python处理他们之间的关联。

3、select_related()的效率要高于prefetch_related()。因此,最好在能用select_related()的地方尽量使用它,也就是说,对于ForeignKey字段,避免使用prefetch_related()。

因为 select_related() 总是在单次 SQL 查询中解决问题,而 prefetch_related() 会对每个相关表进行 SQL 查询,因此 select_related() 的效率通常比后者高

4、可以在调用prefetch_related之前调用select_related,并且Django会按照你想的去做:先select_related,然后利用缓存到的数据prefetch_related。然而一旦prefetch_related已经调用,select_related将不起作用。