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()指定对应的库名称即可。

     
  • 相关阅读:
    10. Regular Expression Matching
    9. Palindrome Number (考虑负数的情况)
    8. String to Integer (整数的溢出)
    7. Reverse Integer (整数的溢出)
    LeetCode Minimum Size Subarray Sum
    LeetCode Course Schedule II
    Linux 文件缓存 (一)
    LeetCode Tries Prefix Tree
    Linux : lsof 命令
    LeetCode Binary Tree Right Side View
  • 原文地址:https://www.cnblogs.com/yonguo123/p/9712963.html
Copyright © 2011-2022 走看看