Django程序的优化技巧

 更新时间:2021年4月29日 20:01  点击:1717

友情提示:

过度性能优化是没有必要甚至有害的,因为花大力气带来的毫秒级的响应提升你的用户可能根本感知不到,毕竟开发人员的时间也很宝贵。

性能优化指标

在对一个Web项目进行性能优化时,我们通常需要评价多个指标:

  • 响应时间
  • 最大并发连接数
  • 代码的行数
  • 函数调用次数
  • 内存占用情况
  • CPU占比

其中响应时间(服务器从接收用户请求,处理该请求并返回结果所需的总的时间)通常是最重要的指标,因为过长的响应时间会让用户厌倦等待,转投其它网站或APP。当你的用户数量变得非常庞大,如何提高最大并发连接数,减少内存消耗也将变得非常重要。

在开发环境中,我们一般建议使用django-debug-toolbar和django-silk来进行性能监测分析。它们提供了每次用户请求的响应时间,并告诉你程序执行过程哪个环节(比如SQL查询)最消耗时间。

对于中大型网站或Web APP而言,最影响网站性能的就是数据库查询部分了。一是反复从数据库读写数据很消耗时间和计算资源,二是当返回的查询数据集queryset非常大时还会占据很多内存。我们先从这部分优化做起。

数据库查询优化

利用Queryset的惰性和缓存,避免重复查询

充分利用Django的QuerySet的惰性和自带缓存特性,可以帮助我们减少数据库查询次数。比如下例中例1比例2要好。因为在你打印文章标题后,Django不仅执行了数据库查询,还把查询到的article_list放在了缓存里,下次可以在其它地方复用,而例2就不行了。

 # 例1: 利用了缓存特性 - Good
 article_list = Article.objects.filter(title__contains="django")
 for article in article_list:
     print(article.title)
 
 # 例2: Bad
 for article in Article.objects.filter(title__contains="django"):
     print(article.title)

但有时我们只希望了解查询的结果是否存在或查询结果的数量,这时可以使用exists()和count()方法,如下所示。这样就不会浪费资源查询一个用不到的数据集,还可以节省内存。

 # 例3: Good
 article_list = Article.objects.filter(title__contains="django")
 if article_list.exists():
     print("Records found.")
 else:
     print("No records")
     
 # 例4: Good
 count = Article.objects.filter(title__contains="django").count()

一次查询所有需要的关联模型数据

假设我们有一个文章(Article)模型,其与类别(Category)是单对多的关系(ForeignKey), 与标签(Tag)是多对多的关系(ManyToMany)。我们需要编写一个article_list的函数视图,以列表形式显示文章清单及每篇文章的类别和标签,你的模板文件可能如下所示:

 {% for article in articles %}
    <li>{{ article.title }} </li>
    <li>{{ article.category.name }}</li>
    <li>
        {% for tag in article.tags.all %}
            {{ tag.name }},
        {% endfor %}
    </li>
 {% endfor %}

在模板里每进行一次for循环获取关联对象category和tag的信息,Django就要单独进行一次数据库查询,造成了极大资源浪费。我们完全可以使用select_related方法和prefetch_related方法一次性从数据库获取单对多和多对多关联模型数据,这样在模板中遍历时Django也不会执行数据库查询了。

 # 仅获取文章数据 - Bad
 def article_list(request):
     articles = Article.objects.all()
     return render(request, 'blog/article_list.html',{'articles': articles, })
 
 # 一次性提取关联模型数据 - Good
 def article_list(request):
     articles = Article.objects.all().select_related('category').prefecth_related('tags')
     return render(request, 'blog/article_list.html', {'articles': articles, })

仅查询需要用到的数据

默认情况下Django会从数据库中提取所有字段,但是当数据表有很多列很多行的时候,告诉Django提取哪些特定的字段就非常有意义了。假如我们数据库中有100万篇文章,需要循环打印每篇文章的标题。如果按例4操作,我们会将每篇文章对象的全部信息都提取出来载入到内存中,不仅花费更多时间查询,还会大量占用内存,而最后只用了title这一个字段,这是完全没有必要的。我们完全可以使用values和value_list方法按需提取数据,比如只获取文章的id和title,节省查询时间和内存(例6-例8)。

 # 例子5: Bad
 article_list = Article.objects.all()
 if article_list:
     print(article.title)
 
 # 例子6: Good - 字典格式数据
 article_list = Article.objects.values('id', 'title')
 if article_list:
     print(article.title)
 
 # 例子7: Good - 元组格式数据
 article_list = Article.objects.values_list('id', 'title')
 if article_list:
     print(article.title)
     
 # 例子8: Good - 列表格式数据
 article_list = Article.objects.values_list('id', 'title', flat=True)
 if article_list:
     print(article.title)

除此以外,Django项目还可以使用defer和only这两个查询方法来实现这一点。第一个用于指定哪些字段不要加载,第二个用于指定只加载哪些字段。

使用分页,限制最大页数

事实前面代码可以进一步优化,比如使用分页仅展示用户所需要的数据,而不是一下子查询所有数据。同时使用分页时也最好控制最大页数。比如当你的数据库有100万篇文章时,每页即使展示100篇,也需要1万页展示给你的用户,这是完全没有必要的。你可以完全只展示前200页的数据,如下所示:

 LIMIT = 100 * 200
 
 data = Articles.objects.all()[:(LIMIT + 1)]
 if len(data) > LIMIT:
     raise ExceededLimit(LIMIT)
 
 return data

数据库设置优化

如果你使用单个数据库,你可以采用如下手段进行优化:

  • 建立模型时能用CharField确定长度的字段尽量不用不用TextField, 可节省存储空间;
  • 可以给搜索频率高的字段属性,在定义模型时使用索引(db_index=True);
  • 持久化数据库连接。

没有持久化连接,Django每个请求都会与数据库创建一个连接,直到请求结束,关闭连接。如果数据库不在本地,每次建立和关闭连接也需要花费一些时间。设置持久化连接时间,仅需要添加CONN_MAX_AGE参数到你的数据库设置中,如下所示:

 DATABASES = {
     ‘default': {
         ‘ENGINE': ‘django.db.backends.postgresql_psycopg2',
         ‘NAME': ‘postgres',
         ‘CONN_MAX_AGE': 60, # 60秒
    }
 }

当然CONN_MAX_AGE也不宜设置过大,因为每个数据库并发连接数有上限的(比如mysql默认的最大并发连接数是100个)。如果CONN_MAX_AGE设置过大,会导致mysql 数据库连接数飙升很快达到上限。当并发请求数量很高时,CONN_MAX_AGE应该设低点,比如30s, 10s或5s。当并发请求数不高时,这个值可以设得长一点,比如60s或5分钟。

当你的用户非常多、数据量非常大时,你可以考虑读写分离、主从复制、分表分库的多数据库服务器架构。这种架构上的布局是对所有web开发语言适用的,并不仅仅局限于Django,这里不做进一步展开了。

缓存

缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存。一般来说从数据库多次把所需要的数据提取出来,要比从内存或者硬盘等一次读出来付出的成本大很多。对于中大型网站而言,使用缓存减少对数据库的访问次数是提升网站性能的关键之一。

视图缓存

 from django.views.decorators.cache import cache_page
 
 @cache_page(60 * 15)
 def my_view(request):
    ...

使用@cached_property装饰器缓存计算属性

对于不经常变动的计算属性,可以使用@cached_property装饰器缓存结果。

缓存临时性数据比如sessions

Django的sessions默认是存在数据库中的,这样的话每一个请求Django都要使用sql查询会话数据,然后获得用户对象的信息。对于临时性的数据比如sessions和messages,最好将它们放到缓存里,也可以减少SQL查询次数。

 SESSION_ENGINE = 'django.contrib.sessions.backends.cache'

模版缓存

默认情况下Django每处理一个请求都会使用模版加载器都会去文件系统搜索模板,然后渲染这些模版。你可以通过使用cached.Loader开启模板缓存加载。这时Django只会查找并且解析你的模版一次,可以大大提升模板渲染效率。

 TEMPLATES = [{
     'BACKEND': 'django.template.backends.django.DjangoTemplates',
     'DIRS': [BASE_DIR / 'templates'],
     'OPTIONS': {
         'loaders': [
            ('django.template.loaders.cached.Loader', [
                 'django.template.loaders.filesystem.Loader',
                 'django.template.loaders.app_directories.Loader',
                 'path.to.custom.Loader',
            ]),
        ],
    },
 }]

注意:不建议在开发环境中(Debug=True)时开启缓存加载,因为修改模板后你不能及时看到修改后的效果。

另外模板文件中建议使用with标签缓存视图传来的数据,便于下一次时使用。对于公用的html片段,也建议使用缓存。

{% load cache %}
 {% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
 {% endcache %}

静态文件

压缩 HTML、CSS 和 JavaScript等静态文件可以节省带宽和传输时间。Django 自带的压缩工具有GzipMiddleware 中间件和 spaceless 模板 Tag。使用Python压缩静态文件会影响性能,一个更好的方法是通过 Apache、Nginx 等服务器来对输出内容进行压缩。例如Nginx服务器支持gzip压缩,同时可以通过expires选项设置静态文件的缓存时间。

以上就是Django程序的优化技巧的详细内容,更多关于Django程序的优化的资料请关注猪先飞其它相关文章!

[!--infotagslink--]

相关文章

  • Mysql效率优化定位较低sql的两种方式

    关于mysql效率优化一般通过以下两种方式定位执行效率较低的sql语句。通过慢查询日志定位那些执行效率较低的 SQL 语句,用 --log-slow-queries[=file_name] 选项启动时, mysqld 会 写一个包含所有执行时间超过 long_quer...2015-11-08
  • Django def clean()函数对表单中的数据进行验证操作

    这篇文章主要介绍了Django def clean()函数对表单中的数据进行验证操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-09
  • MySQL针对Discuz论坛程序的基本优化教程

    过了这么久,discuz论坛的问题还是困扰着很多网友,其实从各论坛里看到的问题总结出来,很关键的一点都是因为没有将数据表引擎转成InnoDB导致的,discuz在并发稍微高一点的环境下就表现的非常糟糕,产生大量的锁等待,这时候如果...2015-11-24
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • 101个MySQL的配置和优化以及备份的经验提示

    MySQL是一个功能强大的开源数据库。随着越来越多的数据库驱动的应用程序,人们一直在推动MySQL发展到它的极限。这里是101条调节和优化 MySQL安装的技巧。一些技巧是针对特定的安装环境的,但这些思路是通用的。我已经把...2013-09-11
  • Angular性能优化之第三方组件和懒加载技术

    这篇文章主要介绍了Angular性能优化之第三方组件和懒加载技术,对性能优化感兴趣的同学,可以参考下...2021-05-11
  • JavaScript提高网站性能优化的建议(二)

    这篇文章主要介绍了JavaScript提高网站性能优化的建议(二)的相关资料,需要的朋友可以参考下...2016-07-29
  • C#程序优化-有效减少CPU占用率

    本文给大家介绍的是C#程序优化的小技巧,通过此方法可以有效的降低CPU的占用率,十分的简单实用,有需要的小伙伴可以参考下。...2020-06-25
  • 利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化)

    这篇文章主要介绍了利用 Chrome Dev Tools 进行页面性能分析的步骤说明(前端性能优化),本文给大家介绍的非常想详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-02-24
  • 网站广告怎么投放最好?首屏广告投放类型优化和广告位布局优化的案例

    网站广告怎么投放最好?一个网站中广告位置最好的是哪几个地方呢,许多的朋友都不知道如何让自己的网站广告收效最好了,今天我们就一起来看看吧。 在说到联盟优化前,...2016-10-10
  • 详解Vue开发网站seo优化方法

    这篇文章主要介绍了Vue开发网站seo优化方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-05-07
  • CocosCreator ScrollView优化系列之分帧加载

    这篇文章主要介绍了CocosCreator ScrollView的优化,从分帧加载进行了讲解,对性能优化感兴趣的同学,一定要看一下...2021-04-15
  • js 正则学习小记之匹配字符串字面量优化篇

    昨天在《js 正则学习小记之匹配字符串字面量》谈到 /"(?:\\.|[^"])*"/ 是个不错的表达式,因为可以满足我们的要求,所以这个表达式可用,但不一定是最好的...2021-05-07
  • 在Django中使用MQTT的方法

    这篇文章主要介绍了在Django中使用MQTT的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-05-10
  • 网站优化的好处是什么

    经过代码优化升级的文件下载与页面显示速度更快;内容能够被更多的用户所访问(包括残障人士等);当浏览器版本更新,或者出现新的网络交互设备时,确保所有应用能够继续正确...2017-07-06
  • 什么是INDEX, NOINDEX, FOLLOW和 NOFOLLOW及优化和特点

    什么是INDEX, NOINDEX, FOLLOW和 NOFOLLOW及优化和特点 The Robots META tag放于后台HTML源代码的标头区(HEAD区)中。   我举一个例子: <HTML>   <HEAD>...2017-07-06
  • 解决导入django_filters不成功问题No module named 'django_filter'

    这篇文章主要介绍了解决导入django_filters不成功问题No module named 'django_filter',具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-15
  • MySQL分页优化

    这篇文章主要为大家详细介绍了MySQL分页优化,内容思路很详细,有意对MySQL分页优化的朋友可以参考一下...2016-04-22
  • Java优化for循环嵌套的高效率方法

    这篇文章主要介绍了Java优化for循环嵌套的高效率方法,帮助大家更好的提升java程序性能,感兴趣的朋友可以了解下...2020-09-14
  • Django项目连接MongoDB的三种方法

    本文主要介绍了Django项目连接MongoDB的三种方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-27