四招改善Diango应用性能

原文地址:http://www.revsys.com/blog/2015/may/06/Django-performance-simple-things/?utm_source=Python+Weekly+Newsletter&utm_campaign=703459f6f9-Python_Weekly_Issue_190_May_7_2015&utm_medium=email&utm_term=0_9e26887fc5-703459f6f9-312748341

如何提高应用的性能往往伴随着一堆令人头疼的难题,但是有时候也并非如此。马上介绍的四个方法能快速简单的提高你的Django应用,恩。。。确实很简单,建议加到你的开发规范中去。

第一招:数据库链接持久化
很多Django开发者在采用PostgresSQL(下面简称PG),尽管连接PG并不是很慢, 但我们仍然可以做点事情提高它的性能。那就是保持数据库链接,从1.6版本开始,Django开始支持数据库链接持久化(译者注:在J2EE中,叫数据库链接池,但是一般需要借助于类似于C3P0这样的库)。

如果不保持数据库链接,那么每次数据库操作都需要建立数据库链接,包括验数据库用户名密码等操作。如果你的数据库服务器和应用服务器不在同一台主机上,即使在网速很快的情况下,每次建立数据库连接仍要消耗大概20~75ms。

只需要在你的数据库配置中使用 CONN_MAX_AGE参数即可启用数据库链接持久化功能,具体如下:
DATABASES = {
    ‘default’: {
        ‘ENGINE’: ‘django.db.backends.postgresql_psycopg2’,
        ‘NAME’: ‘whoohoodb’,
        ‘CONN_MAX_AGE’: 600,
    }}

这样,Django就会将数据库连接保持10分钟,同时又能防止内存泄露或者某个诡异的问题导致数据库链接占用时间太长。当然,你可以把这个值设置的更长一些,但我认为最好不要超过1个小时,这不会带来任何好处。

你可以从Django的文档中获取更多有关数据库链接持久化的信息:https://docs.Djangoproject.com/en/1.8/ref/databases/#persistent-connections

第二招:template加载
一般情况下,Django应用使用以下两种模板加载器:
TEMPLATE_LOADERS = (
    ‘Django.template.loaders.filesystem.Loader’,
    ‘Django.template.loaders.app_directories.Loader’,)
他俩会在每次请求时都会去文件系统中搜索并解析模板文件,这肯定会在一定程度上影响性能。

这时,如果模板可以缓存,那么这个问题就迎刃而解了,恭喜你,你只需要做如下简单的配置,即可达到这一目的:
TEMPLATE_LOADERS = (
    (‘django.template.loaders.cached.Loader’, (
        ‘django.template.loaders.filesystem.Loader’,
        ‘django.template.loaders.app_directories.Loader’,
    )),)

不过如果你在开发环境中,千万不要犯傻去开启这个配置,否则每次模板有改动,你都要重启runserver。

第三招:优化Django session
假设发起一个调查,估计有82%的Django用户都不知道他们的session保存在什么地方,额,好吧,我承认我也是这82%中的一员,但就我们的经验看,这个结论是相当的真实的。

其实默认情况下,Django是将用户session保存在数据库里的(译者注:看看你的数据库里有没有一张django_session数据表),并且你应该定期去清理过期的session,遗憾的是很少有人会这么做。

因此,每次你需要从session中获取用户数据时,实际上都是做了一次SQL查询操作。如果你是那种很在意性能的人,那你可能已经使用了memcached或者Redis来做缓存了。但是实际上,你只需要做如下简单配置,就可以将session保存进缓存,从而不需要每次都去做一次SQL查询了
SESSION_ENGINE = ‘django.contrib.sessions.backends.cache’
假设你可以承受部分用户session信息丢失的话,你可以如下配置:
SESSION_ENGINE = ‘django.contrib.sessions.backends.cached_db’
这样后台缓存在缓存你的数据同时,会定期将数据写入到数据库中(译者注:如果在写入数据库之前,服务器重启会导致缓存中数据丢失,所以作者说如果你可以容忍部分session数据丢失,事实上这个防范也就是本地缓存,如果你的服务是集群的话,建议还是采用Redis方案)

第四招:使用select_related()和prefetch_related()
前面介绍的几个小技巧是不是超容易?这最后一个相对来说就比较复杂了,至少不能通过修改某个配置文件就能搞定了。

可能很多人不会去使用select_related() 和 prefetch_realted()两个方法,但当你的ORM代码发动时,这俩方法可以减少大量的SQL查询操作(译者注:就是类似于SQL缓存吧)

有个很典型的应用场景就是你个BlogPost模型,这个模型有个外键指向User模型实例,然后你需要在页面上展示Blog标的和作者邮箱时,你会写下这样的查询代码: queryset = BlogPost.objects.active 在template中代码如下:
<ul>
{% for post in object_list %}
  <li>{{ post.title }} - {{ post.user.email }}</li>
{% endfor %}
</ul>
这样的话,每次用户访问到这个页面的时候,都会对blog_post表执行一次select语句,并且template循环展示每条blog信息时,post.user.email都会导致一次查询user的SQL查询。

那么我们可以做点什么呢?我们可以让Django的ORM框架在第一次查询时直接通过数据库的JOIN将相关的用户信息查询出来,这样只需要执行一次SQL查询,就可以获取blog和user的信息了。

我们只需要将 queryset = BlogPost.objects.active改写成如下代码即可:
queryset = BlogPost.objects.select_related().active()
尽管这不是通过一个简单的配置就能搞定,但是如你所见,还是很容易实现的吧(译者注:其实这就类似于hibernate中的懒加载和直接加载吧)。

类似的,prefetch_related是针对多对多关系的查询。当你的多对多关系表中的数据量远远小于多对多关系两边实体数量的话(译者注:这个原则是不是比较模糊?),prefetch_related就会极大的提高性能。如果情况不是这样的话,它的作用就小了,或者说比select_related作用要小。

那么你可能会问,我该什么时候用呢?最简单的方法就是在你的项目中使用django-debug-toolbar(https://django-debug-toolbar.readthedocs.org/en/1.3/)去试试哪些典型的数据场景,这里典型的意思是你不要只有一个BlogPost和一个User,试试每个实体都超过100个看看。

那么你所需要做的事情就是使用debug toolbar好好的检查一下你的app,特别是那些查询的次数和时间,如果有某个请求会导致5~10次查询,那么你可能需要好好检查一下是不是该用上prefetch。

通常情况下,如果你的数据库中有100个实体,却发现某个请求导致了200~500多次查询时,那就是个巨大的收获了。尝试着去改改你的查询语句,来降低消耗的查询次数和时间。有时候在某些情况下,你觉得多做几次查询比使用join语句来的更快(译者注:这个更快应该是写代码的速度更快吧?),但是去检查一下查询的时间可能会确保你不会把事情弄糟糕。。。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值