Select_related
select_related
是一个可选的性能提升器,用于在Queryset中进一步访问外键属性时,不会触发数据库。
设计哲学
这也是为什么存在select_related() QuerySet方法。它是一个可选的性能提升器,适用于选择“每个相关对象”的常见情况。
Django官方文档
返回一个QuerySet,它将“跟随”外键关系,在执行查询时选择附加的相关对象数据。这是一个性能提升器,它会导致一个更复杂的查询,但意味着以后使用外键关系不需要进行数据库查询。
正如定义中指出的那样,只有在外键关系中才允许使用select_related
。忽略此规则将使您面临以下异常:
In [21]: print(Book.objects.select_related('name').all().query)
FieldError: Non-relational field given in select_related: 'name'. Choices are: author
让我们通过一个例子来深入了解:
这是我的models.py
。(与之前问的问题一样)
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
__repr__ = __str__
class Book(models.Model):
name = models.CharField(max_length=50)
author = models.ForeignKey(Author, related_name='books', on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
__repr__ = __str__
- 使用
select_related
增强器获取所有书籍及其作者:
In [25]: print(Book.objects.select_related('author').all().explain(verbose=True, analyze=True))
Hash Join (cost=328.50..548.39 rows=11000 width=54) (actual time=3.124..8.013 rows=11000 loops=1)
Output: library_book.id, library_book.name, library_book.author_id, library_author.id, library_author.name
Inner Unique: true
Hash Cond: (library_book.author_id = library_author.id)
-> Seq Scan on public.library_book (cost=0.00..191.00 rows=11000 width=29) (actual time=0.008..1.190 rows=11000 loops=1)
Output: library_book.id, library_book.name, library_book.author_id
-> Hash (cost=191.00..191.00 rows=11000 width=25) (actual time=3.086..3.086 rows=11000 loops=1)
Output: library_author.id, library_author.name
Buckets: 16384 Batches: 1 Memory Usage: 741kB
-> Seq Scan on public.library_author (cost=0.00..191.00 rows=11000 width=25) (actual time=0.007..1.239 rows=11000 loops=1)
Output: library_author.id, library_author.name
Planning Time: 0.234 ms
Execution Time: 8.562 ms
In [26]: print(Book.objects.select_related('author').all().query)
SELECT "library_book"."id", "library_book"."name", "library_book"."author_id", "library_author"."id", "library_author"."name" FROM "library_book" INNER JOIN "library_author" ON ("library_book"."author_id" = "library_author"."id")
正如您所见,使用select_related会在提供的外键上(这里是author
)执行INNER JOIN。
执行时间包括:
为8.562毫秒
另一方面:
- 获取所有书籍及其作者而不使用select_related优化器:
In [31]: print(Book.objects.all().explain(verbose=True, analyze=True))
Seq Scan on public.library_book (cost=0.00..191.00 rows=11000 width=29) (actual time=0.017..1.349 rows=11000 loops=1)
Output: id, name, author_id
Planning Time: 1.135 ms
Execution Time: 2.536 ms
In [32]: print(Book.objects.all().query)
SELECT "library_book"."id", "library_book"."name", "library_book"."author_id" FROM "library_book
正如您所看到的,这只是一个简单的书籍模型上的SELECT查询,其中仅包含author_id。在这种情况下,执行时间为2.536毫秒。
正如在Django doc中提到的:
进一步访问外键属性将导致对数据库的另一个访问:(因为我们还没有它们)
In [33]: books = Book.objects.all()
In [34]: for book in books:
...: print(book.author)
请参考数据库访问优化和QuerySet API参考中的explain()
。
Django数据库缓存:
Django配备了一个强大的缓存系统,可以让您保存动态页面,以便它们不必为每个请求进行计算。为方便起见,Django提供了不同级别的缓存粒度:您可以缓存特定视图的输出,只能缓存难以生成的部分,或者您可以缓存整个站点。
Django还与“下游”缓存(如Squid和基于浏览器的缓存)很好地配合使用。这些是您无法直接控制但可以提供提示(通过HTTP标头)的缓存类型,有关哪些站点部分应该被缓存以及如何缓存。
您应该阅读这些文档,以找出哪个最适合您。
PS1:如需了解计划器及其工作原理的更多信息,请参阅为什么Postgres的计划时间和执行时间差异如此之大?和使用EXPLAIN。
Author.objects.all()
),所以后来执行book.author.name
不应该导致一个查询,因为 Django 可以从作者 queryset 中获取它。但是我意识到这不起作用。 - user3599803