Django查询数据库的性能优化示例代码


Posted in Python onSeptember 24, 2017

前言

Django数据层提供各种途径优化数据的访问,一个项目大量优化工作一般是放在后期来做,早期的优化是“万恶之源”,这是前人总结的经验,不无道理。如果事先理解Django的优化技巧,开发过程中稍稍留意,后期会省不少的工作量。

现在有一张记录用户信息的UserInfo数据表,表中记录了10个用户的姓名,呢称,年龄,工作等信息.

models文件

from django.db import models
 
 class Job(models.Model):
  title=models.CharField(max_length=32)
 
 class UserInfo(models.Model):
  username=models.CharField(max_length=32)
  nickname=models.CharField(max_length=32)
  job=models.ForeignKey(to="Job",to_field="id",null=True)

数据表中记录:

Django查询数据库的性能优化示例代码

另一张数据表记录用户工作的Job表,关联用户的工作字段.

Django查询数据库的性能优化示例代码

要查出每个用户的用户名,呢称和工作等信息

def index(request):
  user_list=models.UserInfo.objects.all()
 
  print(user_list.query)  # 打印查询时使用的语句
  print(type(user_list))  # 打印查询结果的数据类型
 
  for user in user_list:
 
   print("%s-->%s-->%s" %(user.username,user.nickname,user.job.title))
 
  return render(request,'index.html')

打印信息:

SELECT "app01_userinfo"."id", "app01_userinfo"."username", "app01_userinfo"."nickname", "app01_userinfo"."job_id" FROM "app01_userinfo"
<class 'django.db.models.query.QuerySet'>
user1-->user1-->python
user2-->user2-->linux
user3-->user3-->golang
user4-->user4-->python
user5-->user5-->linux
user6-->user6-->golang
user7-->user7-->python
user8-->user8-->linux
user9-->user9-->golang
user10-->user10-->linux

在服务端进行这些操作,这些查询语句的性能是很低的,遍历取出这10个用户的姓名,呢称,工作等信息要在两张数据库中执行11次查询操作.

首先只从UserInfo表中查出所有的用户记录,需要执行一次查询操作.

查询Job数据表,每循环一次用户信息的列表,都需要从Job表中查询一次用户的工作信息.

数据表中总共记录了10条用户记录,所以还需要循环10次才能从Job表中查询完成所有用户的工作信息.所以一共需要执行11次数据库查询操作.

那有没有什么好的方法能够提高数据库查询的效率呢???

def index(request):
  user_list=models.UserInfo.objects.values("username","nickname","job")
 
  print(user_list.query)  # 打印查询时使用的语句
  print(type(user_list))  # 打印查询结果的数据类型
  print("user_list:",user_list)
 
  for user in user_list:
 
   print(user["username"], user["nickname"], user["job"])
 
  return render(request,'index.html')

运行程序,在服务端后台打印信息:

SELECT "app01_userinfo"."username", "app01_userinfo"."nickname", "app01_userinfo"."job_id" FROM "app01_userinfo"
<class 'django.db.models.query.QuerySet'>
user_list: <QuerySet [{'username': 'user1', 'nickname': 'user1', 'job': 1}, {'username': 'user2', 'nickname': 'user2', 'job': 2}, {'username': 'user3', 'nickname': 'user3', 'job': 3}, {'username': 'user4', 'nickname': 'user4', 'job': 1}, {'username': 'user5', 'nickname': 'user5', 'job': 2}, {'username': 'user6', 'nickname': 'user6', 'job': 3}, {'username': 'user7', 'nickname': 'user7', 'job': 1}, {'username': 'user8', 'nickname': 'user8', 'job': 2}, {'username': 'user9', 'nickname': 'user9', 'job': 3}, {'username': 'user10', 'nickname': 'user10', 'job': 2}]>
user1 user1 1
user2 user2 2
user3 user3 3
user4 user4 1
user5 user5 2
user6 user6 3
user7 user7 1
user8 user8 2
user9 user9 3
user10 user10 2

可以看到,查询的结果user_list依然是一个QuerySet,但这个对象集合内部却是一个字典.

而且这次的查询只执行了两次数据库查询操作.

通过这种方式,只需要两次查询就能得到想要的数据,优化了数据库的查询效率.

Django数据库优化操作之select_related主动联表查询

上面的例子里,取对象集合的时候,难道只能查询当前数据表,不能查询其他数据表吗??

当然不是,在这里还可以使用select_related这个方法.

在第一次查询的时候,在all()后面加上一个select_related来做主动的联表查询.

在创建这两张数据表时,job在UserInfo数据表中是做为一个ForeignKey存在的,所以加上select_related后不仅只查询到了UserInfo数据库的记录,同时也查询了Job数据表中的记录.

def index(request):
  user_list=models.UserInfo.objects.all().select_related("job")
 
  print(user_list.query)  # 打印查询时使用的语句
  print(type(user_list))  # 打印查询结果的数据类型
  print("user_list:",user_list)
 
  for user in user_list:
 
   print("%s-->%s-->%s" %(user.username,user.nickname,user.job.title))
 
  return render(request,'index.html')

服务端打印结果

SELECT "app01_userinfo"."id", "app01_userinfo"."username", "app01_userinfo"."nickname", "app01_userinfo"."job_id", "app01_job"."id", "app01_job"."title" FROM "app01_userinfo" LEFT OUTER JOIN "app01_job" ON ("app01_userinfo"."job_id" = "app01_job"."id")
<class 'django.db.models.query.QuerySet'>
user_list: <QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
user1-->user1-->python
user2-->user2-->linux
user3-->user3-->golang
user4-->user4-->python
user5-->user5-->linux
user6-->user6-->golang
user7-->user7-->python
user8-->user8-->linux
user9-->user9-->golang
user10-->user10-->linux

查看打印出来的查询语句,其中有

"FROM "app01_userinfo" LEFT OUTER JOIN "app01_job" ON ("app01_userinfo"."job_id" = "app01_job"."id")"

用来做联表查询,只需要一次就可以查询所有的数据了.

同样的,如果还想继续联表,例如在Job表中再加一个外键字段desc,只需要在查询语句中把desc加入进来就可以了

user_list=models.UserInfo.objects.all().select_related("job__desc")

这样一来就把三张表联系起来做联表查询了,但是一定要确保所加的字段为ForeignKey.

如果使用类似models.UserInfo.objects.all()语句进行查询时,不要做跨表查询,只查询当前表中有的数据,否则查询语句的性能会下降很多.

如果想查其他表中的数据,就加上select_related(ForeignKey字段名);

如果想取多个ForeignKey字段的数据,则可以使用select_related(ForeignKey字段1,ForeignKey字段2,...)

联表查询操作性能也会降低,select_related就是用来做主动联表查询的.

Django数据库优化操作之perfetch_related非主动联表查询

perfetch_related方法是既非主动联表查询,又不进行很多查询语句的一种折衷方案

修改视图函数index

def index(request):
 
  user_list=models.UserInfo.objects.all().prefetch_related("job")
 
  print(user_list.query)  # 打印查询时使用的语句
  print(type(user_list))  # 打印查询结果的数据类型
  print("user_list:",user_list)
 
  for user in user_list:
 
   print("%s-->%s-->%s" %(user.username,user.nickname,user.job.title))
 
  return render(request,'index.html')

后端打印结果

SELECT "app01_userinfo"."id", "app01_userinfo"."username", "app01_userinfo"."nickname", "app01_userinfo"."job_id" FROM "app01_userinfo"
<class 'django.db.models.query.QuerySet'>
user_list: <QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
user1-->user1-->python
user2-->user2-->linux
user3-->user3-->golang
user4-->user4-->python
user5-->user5-->linux
user6-->user6-->golang
user7-->user7-->python
user8-->user8-->linux
user9-->user9-->golang
user10-->user10-->linux

使用prefetch_related方法未联表执行两次查询操作

先查询用户表中的所有数据,把用户表中所有的job_id全部查询出来,并执行去重操作;

结果查询出用户的3种工作,接下来执行"select"语句查询"Job"数据表中的"title"字段

这样一来就只执行了两次数据表的查询操作

在prefetch_related方法中加入一个字段"job",执行了两次数据库查询操作;

如果再加一个字段,则会再多加一次数据为操作操作.

Django数据库优化操作之only方法

def index(request):
  user_list=models.UserInfo.objects.all().only("username")
 
  print(user_list.query)  # 打印查询时使用的语句
  print(type(user_list))  # 打印查询结果的数据类型
  print("user_list:",user_list)
 
  for user in user_list:
 
   print("%s-->%s" %(user.username,user.nickname))
 
  return render(request,'index.html')

服务端后台打印信息

SELECT "app01_userinfo"."id", "app01_userinfo"."username" FROM "app01_userinfo"
<class 'django.db.models.query.QuerySet'>
user_list: <QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
user1-->user1
user2-->user2
user3-->user3
user4-->user4
user5-->user5
user6-->user6
user7-->user7
user8-->user8
user9-->user9
user10-->user10

执行查询操作的时候加上only方法,其查询结果还是一个对象集合,但是从打印出的查询语句可以看到,执行查询操作时只查询了用户的id字段和username字段,并没有查询nickname字段.

但是在后面的循环中,又可以打印用户的nikename信息.为什么呢,因为又执行了一次查询的请求操作.由此得知,查询操作使用了only方法,在only方法中加入哪个查询字段,在后面就使用哪个查询字段.

加only参数是从查询结果中只取某个字段,而另外一个defer方法则是从查询结果中排除某个字段

Django数据库优化操作之defer方法

修改index视图函数

def index(request):
 user_list=models.UserInfo.objects.all().defer("username")

 print(user_list.query)  # 打印查询时使用的语句
 print(type(user_list))  # 打印查询结果的数据类型
 print("user_list:",user_list)

 for user in user_list:

  print("%s" % user.nickname)

 return render(request,'index.html')

服务端打印信息

SELECT "app01_userinfo"."id", "app01_userinfo"."nickname", "app01_userinfo"."job_id" FROM "app01_userinfo"
<class 'django.db.models.query.QuerySet'>
user_list: <QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>
user1
user2
user3
user4
user5
user6
user7
user8
user9
user10

通过打印的查询语句可以知道,使用defer方法后,只从数据库中查询了用户的id字段和用户的nickname字段操作,并没有查询username字段,由此也可以提高Django查询数据库的性能.

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python多进程编程技术实例分析
Sep 16 Python
python实现在无须过多援引的情况下创建字典的方法
Sep 25 Python
举例讲解Python设计模式编程的代理模式与抽象工厂模式
Jan 16 Python
Python实现二分查找与bisect模块详解
Jan 13 Python
windows上安装Anaconda和python的教程详解
Mar 28 Python
pandas的object对象转时间对象的方法
Apr 11 Python
Python pyinotify模块实现对文档的实时监控功能方法
Oct 13 Python
Pandas 按索引合并数据集的方法
Nov 15 Python
python版本五子棋的实现代码
Dec 11 Python
windows下python虚拟环境virtualenv安装和使用详解
Jul 16 Python
Django CBV模型源码运行流程详解
Aug 17 Python
python入门学习关于for else的特殊特性讲解
Nov 20 Python
python学习教程之使用py2exe打包
Sep 24 #Python
python数据结构之列表和元组的详解
Sep 23 #Python
Python字符串和字典相关操作的实例详解
Sep 23 #Python
使用Turtle画正螺旋线的方法
Sep 22 #Python
详谈python read readline readlines的区别
Sep 22 #Python
Python实现针对含中文字符串的截取功能示例
Sep 22 #Python
Python实现二维数组按照某行或列排序的方法【numpy lexsort】
Sep 22 #Python
You might like
完美解决thinkphp验证码出错无法显示的方法
2014/12/09 PHP
php实现阿拉伯数字和罗马数字相互转换的方法
2015/04/17 PHP
QQ邮箱的一个文本编辑器代码
2007/03/14 Javascript
Javascript的闭包
2009/12/31 Javascript
JavaScript 反科里化 this [译]
2012/09/20 Javascript
javascript创建createXmlHttpRequest对象示例代码
2014/02/10 Javascript
在Node.js中使用HTTP上传文件的方法
2015/06/23 Javascript
深入浅析同源策略和跨域访问
2015/11/26 Javascript
解决jQuery上传插件Uploadify出现Http Error 302错误的方法
2015/12/18 Javascript
JavaScript截取、切割字符串的技巧
2016/01/07 Javascript
整理关于Bootstrap排版的慕课笔记
2017/03/29 Javascript
jQuery中each方法的使用详解
2018/03/18 jQuery
js实现文件上传功能 后台使用MultipartFile
2018/09/08 Javascript
微信小程序swiper实现滑动放大缩小效果
2018/11/15 Javascript
vue实现PC端录音功能的实例代码
2019/06/05 Javascript
javascript设计模式 ? 建造者模式原理与应用实例分析
2020/04/10 Javascript
Vue Elenent实现表格相同数据列合并
2020/11/30 Vue.js
使用PDB模式调试Python程序介绍
2015/04/05 Python
Python中用startswith()函数判断字符串开头的教程
2015/04/07 Python
qpython3 读取安卓lastpass Cookies
2016/06/19 Python
python实现可以断点续传和并发的ftp程序
2016/09/13 Python
Python 新建文件夹与复制文件夹内所有内容的方法
2018/10/27 Python
这可能是最好玩的python GUI入门实例(推荐)
2019/07/19 Python
使用numpngw和matplotlib生成png动画的示例代码
2021/01/24 Python
HTML文本属性&amp;颜色控制属性的实现
2019/12/17 HTML / CSS
Ralph Lauren英国官方网站:Ralph Lauren UK
2018/04/03 全球购物
学前教育学生自荐信范文
2013/12/31 职场文书
小型女装店的创业计划书
2014/01/09 职场文书
旅游管理毕业生自荐书
2014/02/02 职场文书
春节晚会主持词
2014/03/24 职场文书
工商行政管理专业求职书
2014/05/23 职场文书
汽车运用工程专业求职信
2014/06/18 职场文书
教师节简报
2015/07/20 职场文书
抖音短视频(douyin)去水印工具的实现代码
2021/03/30 Javascript
Matplotlib可视化之添加让统计图变得简单易懂的注释
2021/06/11 Python
Python实现提取PDF简历信息并存入Excel
2022/04/02 Python