zoukankan      html  css  js  c++  java
  • Django性能优化

    Django是一个基于Python的网站开发框架,一个很重要的特点就是Battery Included,简单来说就是包含了常规开发中所需要的一切东西,包括但不限于完整的ORM模型、中间件、会话处理、模板语言、路由映射、管理员站点等,大大提高了开发者的开发体验,今天要谈的东西便是属于Django ORM性能优化这块的内容。

    一,数据库连接

    django1.6以后已经内置了数据库持久化连接,默认不使用持久化连接,每一个网站的请求都会与数据库建立一个连接。如果数据库不在本地,尽管网速很快,这也将花费20-75ms。

    设置持久化连接,仅需要添加CONN_MAX_AGE参数到你的数据库设置中:

    DATABASES = {
        ‘default’: {
            ‘ENGINE’: ‘django.db.backends.postgresql_psycopg2’,
            ‘NAME’: ‘whoohoodb’,
            ‘CONN_MAX_AGE’: 600,
        }
    }

    通过这样的设置,我们设置的持久化连接每次都将存活10分钟 ,这有助于减少内存泄漏或导致一种片状连接的问题。 
    你也可设置更长的时间,但是我不要设置超过1个小时,因为这样获得的效果不会太好。你可以从django的帮助文档中获取详细信息:https://docs.djangoproject.com/en/1.8/ref/databases/#persistent-connections

    二,索引

    对于经常使用的过滤字段,应考虑加上索引以提升查询性能。当然索引的添加策略是一个很复杂的命题,但是在大多数情况下加上它是没错的。

    三,select_related() 和 prefetch_related()

    在实际的开发中,模型之间经常存在复杂的关联关系。在数据量较大的情况下,默认的查询可能面临潜在的性能问题。今天我们就分享一下Django ORM的查询优化。

    首先需要明确一点:

    Queryset是惰性求值的。

    在Django中,所有的Queryset都是惰性的,意思是当创建一个查询集的时候,并没有跟数据库发生任何交互。因此我们可以对查询集进行级联的filter等操作,只有在访问Queryset的内容的时候,Django才会真正进行数据库的访问。而多频率、复杂的数据库查询往往是性能问题最大的根源。

    为了方便说明,我们定义以下model:

    1 class A(models.Model):
    2     foo = models.IntegerFiled()
    3 
    4 class B(models.Model):
    5     a = models.ForeignKey(A, related_name='bs')

    关联关系中,外键的查询依然是惰性的。当我们通过外键获取一个关联对象的时候,实际上默认获取的是关联对象的ID。这种情况适用于只需要ID而不需要实际的关联对象的场景,这点在Django的文档中有相关说明:

    If you only need a foreign key value, use the foreign key value that is already on the object you’ve got, rather than getting the whole related object and taking its primary key.

    不过我们实际开发中,往往需要访问到外键对象的其他属性。如果按照默认的查询方式去遍历取值,那么会造成多次的数据库查询,效率可想而知。

    select_related和prefetch_related正是为了解决这个问题,他们可以达到这样的目的:在查询对象集合的时候,把指定的外键对象也一并完整查询加载,避免后续的重复查询。

    因此,我们可以通过下面的方式来获取B的外键关系对象A的信息:

    1 b = B.objects.select_related('a').all()
    2 
    3 for temp_b in b:
    4     print temp_b.a.foo

    以上方式,实际上只会触发一次数据库查询,会极大的提升查询性能。

    prefetch_related效果和select_related类似,不过使用的场景不同:

        1,select_related适用于外键和多对一的关系查询;

        2,prefetch_related适用于一对多或者多对多的查询。

    四,iterator()

    如果需要一次查出大批量的数据,Django默认会将数据缓存在内存中,这会造成较大的内存占用。

    这里我们推荐使用iterator(),它会把结果缓存缓存在磁盘或swap中,使用的时候再一条一条取出,节省内存使用。

    五,尽量减少数据库查询次数

    单一动作(如:同一个页面)需要多次连接数据库时,最好一次性取出所有需要的数据,减少连接数据库次数;

    此类需求推荐使用QuerySet.select_related() (主动连表)和 prefetch_related()(被动连表);

    相反,别取出你不需要的东西,模版templates里往往只需要实体的某几个字段而不是全部,这时QuerySet.values() 和 values_list(),对你有用,它们只取你需要的字段,返回字典dict和列表list类型的东西,在模版里够用即可,这可减少内存损耗,提高性能;

    同样QuerySet.defer()only()对提高性能也有很大的帮助,一个实体里可能有不少的字段,有些字段包含很多元数据,比如博客的正文,很多字符组成,Django获取实体时(取出实体过程中会进行一些python类型转换工作),我们可以延迟大量元数据字段的处理,只处理需要的关键字段,这时QuerySet.defer()就派上用场了,在函数里传入需要延时处理的字段即可;而only()和defer()是相反功能;

    使用QuerySet.count()代替len(queryset),虽然这两个处理得出的结果是一样的,但前者性能优秀很多。同理判断记录存在时,QuerySet.exists()比if queryset实在强得太多了。

    六,配置主从,读写分离

    增加主从数据库配置:

    DATABASES = {
          'default': {
              'ENGINE': 'django.db.backends.mysql',
              'NAME': 'dailyfresh',
              'HOST': '192.168.243.193', # MySQL数据库地址(主)
              'PORT': '3306',
              'USER': 'root',
              'PASSWORD': 'mysql',
          },
          'slave': {
                  'ENGINE': 'django.db.backends.mysql',
                  'NAME': 'dailyfresh',
                  'HOST': '192.168.243.189', # MySQL数据库地址(从)
                  'PORT': '3306',
                  'USER': 'root',
                  'PASSWORD': 'mysql',
              }
      }

    使用的时候用using()指定对应的库名称即可。

     
  • 相关阅读:
    函数——返回值不止一个的情况(解一元二次方程)
    排序方法:去掉最高最低分求平均分 函数
    丢羊 递归
    网吧充值 函数+Swich+ArrayList版
    对战游戏自己简单版+大神版
    简单Struct+ArrayList选择买东西:购物车思路
    推箱子 复杂版(多个箱子)
    stuct+ArrayList的for用法
    Struct+ArrayList冒泡排序。例
    c# 穷举
  • 原文地址:https://www.cnblogs.com/yonguo123/p/9712963.html
Copyright © 2011-2022 走看看