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 相关文章推荐
linux系统使用python监控apache服务器进程脚本分享
Jan 15 Python
简单使用Python自动生成文章
Dec 25 Python
python用plt画图时,cmp设置方法
Dec 13 Python
Python3标准库总结
Feb 19 Python
如何利用Python模拟GitHub登录详解
Jul 15 Python
解决在pycharm运行代码,调用CMD窗口的命令运行显示乱码问题
Aug 23 Python
Python3 中作为一等对象的函数解析
Dec 11 Python
python实现拼图小游戏
Feb 22 Python
如何在Python对Excel进行读取
Jun 04 Python
Python爬虫教程知识点总结
Oct 19 Python
python 对图片进行简单的处理
Jun 23 Python
Python办公自动化之教你如何用Python将任意文件转为PDF格式
Jun 28 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
php下通过POST还是GET来传值
2008/06/05 PHP
PHP三层结构(上) 简单三层结构
2010/07/04 PHP
php下清空字符串中的HTML标签的代码
2010/09/06 PHP
简单解决新浪SAE无法上传文件的问题
2015/05/13 PHP
PHP利用Socket获取网站的SSL证书与公钥
2017/06/18 PHP
javascript 复杂的嵌套环境中输出单引号和双引号
2009/05/26 Javascript
JavaScript Sort 表格排序
2009/10/31 Javascript
再谈javascript 动态添加样式规则 W3C校检
2009/12/25 Javascript
Date对象格式化函数代码
2010/07/17 Javascript
js跳转页面方法实现汇总
2014/02/11 Javascript
利用函数的惰性载入提高javascript代码执行效率
2014/05/05 Javascript
jquery实现的鼠标下拉滚动置顶效果
2014/07/24 Javascript
js实现楼层效果的简单实例
2016/07/15 Javascript
JQuery 动态生成Table表格实例代码
2016/12/02 Javascript
jQuery与js实现颜色渐变的方法
2016/12/30 Javascript
通过AngularJS实现图片上传及缩略图展示示例
2017/01/03 Javascript
Ionic3 UI组件之Gallery Modal详解
2017/06/07 Javascript
前端构建工具之gulp的配置与搭建详解
2017/06/12 Javascript
iview中Select 选择器多选校验方法
2018/03/15 Javascript
详解使用Next.js构建服务端渲染应用
2018/07/10 Javascript
深入探索VueJS Scoped CSS 实现原理
2019/09/23 Javascript
vue 实现cli3.0中使用proxy进行代理转发
2019/10/30 Javascript
一起来了解一下JavaScript的预编译(小结)
2021/03/01 Javascript
Python实现冒泡,插入,选择排序简单实例
2014/08/18 Python
Python内置模块hashlib、hmac与uuid用法分析
2018/02/12 Python
Python同步遍历多个列表的示例
2019/02/19 Python
详解Django项目中模板标签及模板的继承与引用(网站中快速布置广告)
2019/03/27 Python
python Matplotlib数据可视化(1):简单入门
2020/09/30 Python
使用AJAX和Django获取数据的方法实例
2020/10/25 Python
财务会计人员岗位职责
2013/11/30 职场文书
大学生演讲稿范文
2014/01/11 职场文书
任命书范本大全
2014/06/06 职场文书
上班迟到检讨书范文300字
2014/11/02 职场文书
学子宴致辞大全
2015/07/27 职场文书
《钢铁是怎样炼成的》高中读后感
2019/08/07 职场文书
Vue3中toRef与toRefs的区别
2022/03/24 Vue.js