django 优化方式

前言

对于网站和Web APP来说,相同的类型的产品,响应速度越好,那么用户量就越高。不可否认的是,响应速度是用户黏粘性最好的方式之一,但往往不知道如何下手解决,希望这篇文章可以给予你一些思路
对于网站和Web APP来说最影响网站性能的就是数据库查询了,因为,而查询返回的数据集非常大时还会占据很多内存。这里从django orm的角度来探索数据库查询的优化

一、依据减少缓存的角度优化:利用QuerySet惰性

网站和Web APP,对于数据的常规处理方式肯定是数据库存储查询,反复从数据库读写数据很耗时间和计算资源。也因此开发者们设计制作出了多个数据库连接方式,其中在Django框架中ORM(语法有哪些?)占很大比重。通过它可以使用filter, exclude, get等方法进行数据库查询,从数据库中查询出来的结果一般是一个集合,这个集合叫就做 QuerySet。QuerySet是惰性的!QuerySet自带缓存!

注:Object Relational Mapping,即对象关系映射,是在pymysq之上又进行了一层封装,对于数据的操作,我们无需再去编写原生sql,取代代之的是基于面向对象的思想去编写类、对象、调用相应的方法等,ORM会将其转换/映射成原生SQL然后交给pymysql执行

  1. 惰性即为当被执行(print、if、len)时才会进行数据库查询,这样做的目的是防止无效数据库操作。减少数据库交互
# 惰性查询:如果只是书写了orm语句,在后面根本没有用到该语句所查询出来的参数,那么orm会自动识别出来,直接不执行。# 举例:res = models.Book.objects.all()  # 这时orm是不会走数据库的print(res)   # 只有当要用到的上述orm语句的结果时,才回去数据库查询。
  1. QuerySet被执行后,其查询结果会载入内存并保存在QuerySet内置的cache中。再次使用就不需要重新去查询了。减少缓存
  2. 如果查出的 QuerySet只用一次,可以使用 iterator() 去来防止占用太多的内存
  3. if与exists()都可以判断查询结果是否存在,但两者使用却又很大的不相同。if会触发整个queryset的缓存,而exists()只会返回True或False检视查询结果是否存在而不会缓存查询结果
  4. len()与count()方法均能统计查询结果数量。count()是从数据库层面直接获取查询结果数量而不需要返回整个queryset数据集一般来说会更快。len()会导致queryset的执行,需要先将整个数据集载入内存方可计算,但如果queryset数据集已经缓存在内存当中了len()则会更快
  5. 当查询到的queryset非常大时,会占用大量的内存,使用values和values_list按需提取数据(1个或个别多字段,而非全字段)。values和values_list返回的是字典形式字符串数据,而不是对象集合
  6. only(A)包含与,查A走一次数据库,查B走多次数据库。defer(A)不包含与,查A走多次数据库,查B走一次数据库
  7. 相比于使用save()方法,update()不需要先缓存整个queryset
  8. aggregate和annotate方法主要用于组合查询,我们使用aggregate完成对查询集(queryset)的某些字段进行计算,使用annotate进行分组并追加统计字段,如
class Student(models.Model):name = models.CharField(max_length=20)age = models.IntegerField()hobbies = models.ManyToManyField(Hobby)class Hobby(models.Model):name = models.CharField(max_length=20)from django.db.models import Max, Min, Avg, Sum, Count
#####################aggregate应用###############################
# 学生平均年龄, 自定义key
Student.objects.aggregate(average_age = Avg('age'))  # { 'average_age': 12 }# 同时获取学生年龄均值, 最大值和最小值, 返回字典 
Student.objects.aggregate(Avg('age‘), Max('age‘), Min('age‘))
# { 'age__avg': 12, 'age__max': 18, 'age__min': 6, }# 根据Hobby反查学生最大年龄。查询字段student和age间有双下划线
Hobby.objects.aggregate(Max('student__age'))  # { 'student__age__max': 12 }#####################annotate应用###############################
# 按学生分组,统计每个学生爱好数量,并自定义key
Student.objects.annotate(hobby_count_by_student=Count('hobbies'))# 按爱好分组,再统计每组学生最大年龄
Hobby.objects.annotate(Max('student__age'))#####################annotate&filter应用###############################
# 先按爱好分组,再统计每组学生数量, 然后筛选出学生数量大于1的爱好。
Hobby.objects.annotate(student_num=Count('student')).filter(student_num__gt=1)# 先按爱好分组,筛选出以'd'开头的爱好,再统计每组学生数量。
Hobby.objects.filter(name__startswith="d").annotate(student_num=Count('student‘))#####################annotate&order_by应用###############################
# 先按爱好分组,再统计每组学生数量, 然后按每组学生数量大小对爱好排序。
Hobby.objects.annotate(student_num=Count('student‘)).order_by('student_num')# 统计最受学生欢迎的5个爱好。
Hobby.objects.annotate(student_num=Count('student‘)).order_by('-student_num')[:5]#####################annotate&values应用###############################
# 按学生名字分组,统计每个学生的爱好数量。
Student.objects.values('name').annotate(Count('hobbies'))你还可以使用values方法从annotate返回的数据集里提取你所需要的字段,如下所示:
# 按学生名字分组,统计每个学生的爱好数量。
Student.objects.annotate(hobby_count=Count('hobbies')).values('name', 'hobby_count')
  1. select_related&prefetch_related使用

假设现在有文章表(Article)、类别表(Category)、标签表(Tag)。它们关系是文章与类别是一对多关系,文章与标签是多对多关系

  • 常规写法,错倒是没错。然而使用Article.objects.all()查询得到的只是Article表的数据,并没有包含Category表和Tag表的数据。因此每一次打印article.category.name和tag.name都会重新去查询一遍Category表和Tag表,造成了很大不必要的浪费
# 查询类别、标签信息
articles = Article.objects.all()
for article in articles:print(article.title)print(article.category.name)for tag in article.tags.all():print(tag.name)
  • 标准写法,select_related可查询一对多、一对一的关系,不可以多对多关系。处理的方式是inner join连表。实现打印类别是无需再去查数据库,因为数据已经一次性获取出来了
# 查询类别
articles = Article.objects.all().select_related('category')# 获取id=13的文章对象同时,获取其相关category信息
Article.objects.select_related('category').get(id=13)# 获取id=13的文章对象同时,获取其相关作者名字信息
Article.objects.select_related('author__name').get(id=13)# 获取id=13的文章对象同时,获取其相关category和相关作者名字信息。下面方法等同
Article.objects.select_related('category', 'author__name').get(id=13)
Article.objects.select_related('category').select_related('author__name').get(id=13)# 使用select_related()可返回所有相关主键信息,all()非必需
Article.objects.all().select_related()# 获取Article信息同时获取blog信息,filter方法和selected_related方法顺序不重要
Article.objects.filter(pub_date__gt=timezone.now()).select_related('blog')
Article.objects.select_related('blog').filter(pub_date__gt=timezone.now())
  • 标准写法,prefetch_related弥补多对多下的数据查询
# 查询类别及标签
articles = Article.objects.all().select_related('category').prefecth_related('tags')# 文章列表及每篇文章的tags对象名字信息
Article.objects.all().prefetch_related('tags__name')# 获取id=13的文章对象同时,获取其相关tags信息
Article.objects.prefetch_related('tags').get(id=13)用Prefetch方法可以给prefetch_related方法额外添加额外条件和属性
# 获取文章列表及每篇文章相关的名字以P开头的tags对象信息
Article.objects.all().prefetch_related(Prefetch('tags', queryset=Tag.objects.filter(name__startswith="P"))
)# 文章列表及每篇文章的名字以P开头的tags对象信息, 放在article_p_tag列表
Article.objects.all().prefetch_related(Prefetch('tags', queryset=Tag.objects.filter(name__startswith="P")), to_attr='article_p_tag'
)
  1. F函数不引入内存
  • 更新数据时
1
article = Article.objects.get(title='文章2')
article.thumb_count += 1
article.save()2 使用F()函数
Article.objects.filter(title='文章1').update(thumb_count=F('thumb_count')+1)# 很明显使用F()函数的执行效率会更高,只需要一条sql完全的数据库操作,而例1则需要先查询,缓存,然后再更新
# 例1的方法是存在竞态条件的,如第一个线程完成取值、更新值、保存新值,而第二个线程操作还是使用就的值来进行操作,使用F()函数的话,因为是数据库层面的原子操作,第二个线程再来取值那也是取到更新后的值了
  • 表达式应用时
# 同一数据不同字段比较
article = Article.objects.filter(thumb_count__gt=F('view_count'))# 两个操作数都是常数和F()函数的加、减、乘、除、取模、幂计算等算术操作
article = Article.objects.filter(view_count__gt=F('thumb_count') * 2)# 配合annotate使用
article = Article.objects.annotate(all_count=F('view_count') + F('thumb_count'))

二、利用索引

合适的索引可以加快数据的检索速度。无论是在Django还是在原生SQL查询上都支持检查某条语句是否有用到索引,语法为explain

2.1、Django中

# 统计一个查询所消耗的执行时间,更好地优化查询结果
Blog.objects.filter(title='My Blog').explain(verbose=True)

2.2、原生SQL中

explain select * from user where user_no ='00022139'

三、ORM相较于原生SQL语法有性能欠缺

注:Django也支持原生SQL语法:raw

3.1、raw语法实现

3.2、游标实现

from django.db import connectionwith connection.cursor() as cursor:
cursor.execute('select * from user')
data = cursor.fetchall()

四、结束!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/30628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LC-杨辉三角-记忆版

LC-杨辉三角-记忆版 上一篇:LC-杨辉三角 上一篇讲了杨辉三角的算法,不过之前的算法存在一个问题,比如: a[5][3] a[4][2] a[4][3] a[5][4] a[4][3] a[4][4]我们可以看到计算a[5][3]和a[5][4]时都需要a[4][3]的值&#xff0c…

如何让PPT看起来规整统一

一、字体 常见问题:字体风格太多、文字可读性差、页面风格不匹配 1.使用文字的几个原则 (1)一份PPT最多使用两种中文字体 比如首页大标题宋体、正文黑体、其他页标题黑体加粗。通过粗细、字号、不同颜色背景等区分不同层级。注意 使用粗体…

【React学习】—jsx语法规则(三)

【React学习】—jsx语法规则(三) 一、jsx语法规则: 1、定义虚拟DOM,不要写引号, 2、标签中混入JS表达式要用{} 3、样式的类名指定不要用class,要用className 4、内联样式,要用style{{key:value}…

AWS中Lambda集成SNS

1.创建Lambda 在Lambda中,创建名为AWSSNSDemo的函数 use strict console.log(loading function); var aws require(aws-sdk); var docClient new aws.DynamoDB.DocumentClient(); aws.config.regionap-southeast-1;exports.handler function(event,context,cal…

一台电脑B用网线共享另外一台电脑A的WiFi网络,局域网其它电脑C怎么访问电脑B服务

环境: 电脑A:联想E14笔记本 系统:WIN10 专业版 局域网IP:192.168.14.111 共享IP:192.168.137.1 电脑B:HP 288pro 台式机 Ubuntu20.04 系统:共享IP:192.168.137.180 电脑A正常连接WIFI,电脑B没有WIFI只有,有线网口,共享电脑A的无线网 (还有一种桥接网络不在本…

pdf怎么转换成jpg图片?这几个转换方法了解一下

pdf怎么转换成jpg图片?转换PDF文件为JPG图片格式在现代工作中是非常常见的需求,比如将PDF文件中的图表、表格或者图片转换为JPG格式后使用在PPT演示、网页设计等场景中。 【迅捷PDF转换器】是一款非常实用的工具,可以将PDF文件转换成多种不同…

I/O 函数/缓存和字节流、占位符、getchar(),putchar()

I/O 函数 C 语言提供了一些函数,用于与外部设备通信,称为输入输出函数,简称 I/O 函数。输入(import)指的是获取外部数据,输出(export)指的是向外部传递数据。 缓存和字节流 严格地…

创建CREATE_STAT_TABLE 统计信息表在达梦和oracle中的使用

达梦 创建CREATE_STAT_TABLE 统计信息表 PROCEDURE CREATE_STAT_TABLE ( STATOWN VARCHAR(128), STATTAB VARCHAR(128), TABLESPACE VARCHAR(128) DEFAULT NULL, GLOBAL_TEMPORARY BOOLEAN DEFAULT FALSE ); 创建普通表的对应系统表的列名字段包括以下: OWNER TABL…

C 语言的逻辑运算符

C 语言的逻辑运算符包括三种: 逻辑运算符可以将两个关系表达式连接起来. Suppose exp1 and exp2 are two simple relational expressions, such as cat > rat and debt 1000 . Then you can state the following: ■ exp1 && exp2 is true only if bo…

MySQL 8.0 事务定义和基本操作

MySQL 事务(Transaction)的四大特性:A、C、I、D A、原子性:(Atomicity) 一个事务是不可分割的最小工作单位。 执行的事务,要么全部成功,要么回滚到执行事务之前的状态。 C、一致…

通讯协议035——全网独有的OPC HDA知识一之聚合(四)平均值

本文简单介绍OPC HDA规范的基本概念,更多通信资源请登录网信智汇(wangxinzhihui.com)。 本节旨在详细说明HDA聚合的要求和性能。其目的是使HDA聚合标准化,以便HDA客户端能够可靠地预测聚合计算的结果并理解其含义。如果用户需要聚合中的自定义功能&…

计算机基础知识一

1、计算机系统组成 1.1 硬件 CPU:中央处理器、计算机核心部件、负责计算任务 内存:记忆功能、存储二进制数,内存是一个字节一个地址。 内存大小换算: 8 bits 1 Byte 1024 Bytes Bytes 1 KB , 1024 KB KB 1 …

putty如何保存session

标准版的putty是不支持保存session的,这是处于安全性的考虑。 不过用起来比较麻烦,可以通过快捷方式的办法来实现保存session。 新建一个快捷方式,指向哪里都可以。 右键 | 属性 | 目标 这里修改下: "C:\Program Files\PuTTY…

java 企业工程管理系统软件源码 自主研发 工程行业适用 em

​ 工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理 工程项目各模块及其功能点清单 一、系统管理 1、数据字典&#…

文盘 Rust -- tokio 绑定 cpu 实践

tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢?这次我们来聊聊这个话题。 首先我们先写一段简单的多任务程序。 use tokio::runtime; pub fn main() {let rt runtime::Builder::new_mu…

【Github】Uptime Kuma:自托管监控工具的完美选择

简介: Uptime Kuma 是一款强大的自托管监控工具,通过简单的部署和配置,可以帮助你监控服务器、VPS 和其他网络服务的在线状态。相比于其他类似工具,Uptime Kuma 提供更多的灵活性和自由度。本文将介绍 Uptime Kuma 的功能、如何使…

Redux中reducer 中为什么每次都要返回新的state!!!

Redux中reducer 中为什么每次都要返回新的state!!! 最近在学习react相关的知识,学习redux的时候遇到看到一个面试题: 如果Redux没返回新的数据会怎样? 这就是要去纠结为什么编写reducer得时候为什么不允许直…

Unity悬浮显示提示内容

在编写unity时,需要实现鼠标在某一个按钮上时,就显示其子物体中对应的下拉菜单,为此编写了一个公共类,对于需要悬浮显示的控件均可挂载此类。代码如下: using UnityEngine; using UnityEngine.EventSystems; using Un…

服装行业多模态算法个性化产品定制方案 | 京东云技术团队

一、项目背景 AI赋能服装设计师,设计好看、好穿、好卖的服装 传统服装行业痛点 • 设计师无法准确捕捉市场趋势,抓住中国潮流 • 上新周期长,高库存滞销风险大 • 基本款居多,难以满足消费者个性化需求 解决方案 • GPT数据…

Python数据分析实战-dataframe指定多列去重(附源码和实现效果)

实现功能 Python数据分析实战-利用df.drop_duplicates(subset[,])对dataframe指定多列去重 实现代码 import pandas as pddata{state:[1,1,2,2,1,2,2],pop:[a,b,c,d,b,c,d]} framepd.DataFrame(data)frameframe.drop_duplicates(subset[pop,state]) print(frame) 实现效果 本…