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
相关文章
|
SQL Python
这个贴子的内容值得好好学习--实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化
感觉要DJANGO用得好,ORM必须要学好,不管理是内置的,还是第三方的ORM。 最最后还是要到SQL。。。。。:( 这一关,慢慢练啦。。   实例详解Django的 select_related 和 prefetch_related 函数对 QuerySet 查询的优化 http://blog.
1167 0
|
1月前
|
监控 安全 应用服务中间件
python中Django入门(四)
python中Django入门(四)
31 0
|
3月前
|
SQL 前端开发 JavaScript
Python 教程之 Django(10)模板
Python 教程之 Django(10)模板
34 0
|
3月前
|
存储 安全 网络协议
Python 教程之 Django(9)对模型中的字段进行验证
Python 教程之 Django(9)对模型中的字段进行验证
30 0
Python 教程之 Django(9)对模型中的字段进行验证
|
14天前
|
安全 数据库 C++
Python Web框架比较:Django vs Flask vs Pyramid
【4月更文挑战第9天】本文对比了Python三大Web框架Django、Flask和Pyramid。Django功能全面,适合快速开发,但学习曲线较陡;Flask轻量灵活,易于入门,但默认配置简单,需自行添加功能;Pyramid兼顾灵活性和可扩展性,适合不同规模项目,但社区及资源相对较少。选择框架应考虑项目需求和开发者偏好。
|
2月前
|
前端开发 关系型数据库 MySQL
基于python+django+vue.js开发的社区养老管理系统
基于python+django+vue.js开发的社区养老管理系统
92 1
|
6天前
|
Python
基于Django的Python应用—学习笔记—功能完善
基于Django的Python应用—学习笔记—功能完善
|
21天前
|
前端开发 测试技术 数据库
【python】为什么使用python Django开发网站这么火?
【python】为什么使用python Django开发网站这么火?
|
1月前
|
中间件 数据安全/隐私保护 Python
python中Django入门(三)
python中Django入门(三)
17 0
|
1月前
|
前端开发 JavaScript Shell
python中Django入门(二)
python中Django入门(二)
13 0