开发者社区> 科技探索者> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

Django查询优化之select_related和prefetch_related

简介:
+关注继续查看

一、select_related查询优化

    select_related通过多表join关联查询,一次性获得所有数据,通过降低数据库查询次数来提升性能,但关联表不能太多,因为join操作本来就比较消耗性能。本文通过Django debug toolbar工具来直观显示查询次数、查询语句,如果不会使用“Django debug toolbar”工具,可以翻看我之前写的博客,从而配置它!

1
2
3
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段')

models.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from django.db import models
 
class Publisher(models.Model):
    name = models.CharField(max_length=30, verbose_name="名称")
    address = models.CharField("地址", max_length=50)
    city = models.CharField('城市', max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
 
    class Meta:
        verbose_name = '出版商'
        verbose_name_plural = verbose_name
 
    def __str__(self):
        return self.name
 
class Author(models.Model):
    name = models.CharField(max_length=30)
    hobby = models.CharField(max_length=20, default="", blank=True)
 
    def __str__(self):
        return self.name
 
class Book(models.Model):
    title = models.CharField(max_length=100, verbose_name="书名")
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher, verbose_name="出版社")
    publication_date = models.DateField(null=True)
    price = models.DecimalField(max_digits=5, decimal_places=2, default=10, verbose_name="价格")
 
    def __str__(self):
        return self.title

views.py

1
2
3
def index(request):
    obj = Book.objects.all()
    return render(request, "index.html"locals())

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>django debug toolbar!</p>
    {% for item in obj %}
        <div>{{ item.title }} {{ item.price }} {{ item.publisher.name }}</div>
    {% endfor %}
</body>
</html>


    当我们没有使用select_related时,在前端模板中,每一次循环就要向数据库发送一次请求,因为我表中数据很少,所有只发起了7次查询,但实际生产中每个表的数据肯定是成千上万的,传统的操作对数据库的性能影响很大!

09869b38c62104efa810056359cf02bd.png


当我们使用select_related连表操作时,请看下例,只发起了两次查询!!!

1
2
3
def index(request):
    obj = Book.objects.all().select_related("publisher")
    return render(request, "index.html"locals())


2bad9015ec1e9623715345e2a7fa6fd0.png-wh_

总结:

  1. select_related主要针一对一和多对一关系进行优化。

  2. select_related使用SQL的JOIN语句进行优化,通过减少SQL查询的次数来进行优化、提高性能。

  3. 可以通过可变长参数指定需要select_related的字段名。也可以通过使用双下划线“__”连接字段名来实现指定的递归查询(也就是外键的外键,多层连表查询)。没有指定的字段不会缓存,没有指定的深度不会缓存,如果要访问的话Django会再次进行SQL查询。


二、prefetch_related查询优化

    prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量,但是实现的方式不一样。后者是通过JOIN语句,在SQL查询内解决问题。但是对于多对多关系,使用SQL语句解决就显得有些不太明智,因为JOIN得到的表将会很长,会导致SQL语句运行时间的增加和内存占用的增加。prefetch_related()的解决方法是,分别查询每个表,然后用Python处理他们之间的关系!

1
models.tb.objects.prefetch_related('外键字段')


我们还是通过上例来举例:

1
2
3
def index(request):
    obj = Book.objects.all().prefetch_related("publisher")
    return render(request, "index.html"locals())



74f361d703e0c38264459d1da47b4c34.png4b515eeaf266edf23749f99a9da796e5.png

    使用prefetch_related优化查询,貌似发起了四次数据库请求,但实际是只有两次的,就是图中划横线的SQL语句,其他两条是session相关的,我们不用理会。我来解释一下prefetch_related是怎么发起请求的,第一步:先拿到book表的所有数据;第二步:通过select .. from ... where ... in (book表中所有出版社的外键ID)。这样通过分别发起两次请求,获取了book表以及和book表相关联的publisher表的数据(并不是所有publisher表数据,只有和book表相关联数据!),然后通过Python处理数据的对应关联。

总结:

  1. prefetch_related主要针一对多和多对多关系进行优化。

  2. prefetch_related通过分别获取各个表的内容,然后用Python处理他们之间的关系来进行优化。


大结局:

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

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

本文转自戴柏阳的博客博客51CTO博客,原文链接http://blog.51cto.com/daibaiyang119/1977637如需转载请自行联系原作者

daibaiyang119

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Python:Django开发环境与生产环境的配置
Python:Django开发环境与生产环境的配置
0 0
Django配合python进行requests请求
Django配合python进行requests请求
0 0
Python基于Django的电影推荐系统和论坛项目完整源码
Python基于Django的电影推荐系统和论坛项目完整源码
0 0
Python+Django实现智慧校园考试比赛系统
Python+Django实现智慧校园考试比赛系统
0 0
基于Python+Django实现药品管理系统
基于Python+Django实现药品管理系统
0 0
Python Django开发 异常及解决办法(一)
该错误表明views.py中没有return一个返回值给前端。
0 0
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Dynamic DDL Adding Structure t
立即下载
Lazy Join Optimizations Without Upfront Statistics
立即下载
Cost-based Query Optimization
立即下载