vieyahn2017 / iBlog

44 stars 0 forks source link

6.2 Django性能优化 #142

Closed vieyahn2017 closed 4 years ago

vieyahn2017 commented 6 years ago

Django性能优化的几种方法

https://www.cnblogs.com/yunxintryyoubest/p/11028691.html

vieyahn2017 commented 4 years ago

1.一次性取出你所需要的数据

单一动作,需要多次连接数据库里的时候,最好一次性取出所有需要的数据,减少连接数据库的次数。此类需求推荐使用QuerySet.select_related()和prefetch_related()

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

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

当然一样的结果,当缓存里面已经存在的时候,就别再滥用count(),exitst(),all()函数了。

2.减少数据库连接的次数

使用queryset.update()和delete(),这两个函数是可以批量处理多条记录的,使他们事半功倍;如果可以,被一条条数据去update delete处理,对于一次性取出来的关联记录,获取外键的时候,直接取关联表的属性,而不是取关联属性,如entry.blog.id优于enrty.blog_id

3.在配置使用相对路径

某些原因使得项目可能常常会被来回的迁移,如果事先没有规划好这种可能性的话,这绝对是一个棘手的问题,有一个极好的技巧能够确保你的Django项目在部署的过程中能够轻松来回迁移,仅仅只需要编写几行代码就可以在你的配置文件settings.py中。、

import  os

BASE_DIR=os.path.dirname(os.path.abspath(__file__)

TEMPLATE_DIRS=(

BASE_DIR+'/templates',

)

4.使用Django-chunks

除了使用Django的富文本编辑器创建模块更日益之外,Django-chunks同样是用于模块中,这个是重用代码块必不可少的工具。

5.使用缓存

如果性能在你的DJango项目中已经成为棘手的问题,那么你将需要使用一些缓存策略,然而Django为缓存提供很多的选择,目前最好的无疑是Memcache,用Django安装memcache非常的简单,如果你使用memcache模块的时候,只要模块安装完成后,你仅仅修改一行配置项,你的Django页面变得轻快起来。

vieyahn2017 commented 4 years ago

6.Django-debug-toolbar

它是一个非常方便的工具,可以深入了解代码的工作以及它花费了多少时间。特别是他可以显示你的页面生成的SQL查询,以及每个人花了多少时间。

一个在github上面的开源项目:https://github.com/dcramer/django-devserver

很棒的一个可视化工具,但是缺点只能处理text/html类型的response,因为是通过中间件修改返回的html代码实现的

解决方法:开源使用这个库,django-debug-panel

在配合链接中最后的chorme插件来使用,就可以查看所有异步请求中的详细信息。

优点:

统计了总的sql查询时间

重复查询的sql的数量,在每条sql详细信息中显示重复的次数。

执行了sql的具体代码位置

sql语句的高亮

sql查询到的数据结果。

配置参考:

# debug_toolbar settings
if DEBUG:
    INTERNAL_IPS = ('127.0.0.1',)
    MIDDLEWARE_CLASSES = (
        # 'debug_toolbar.middleware.DebugToolbarMiddleware',
        'debug_panel.middleware.DebugPanelMiddleware',
    ) + MIDDLEWARE_CLASSES

    INSTALLED_APPS += (
        'debug_toolbar',
        'debug_panel',
    )

if settings.DEBUG:
    import debug_toolbar
    urlpatterns = [
        url(r'^__debug__/', include(debug_toolbar.urls)),
    ] + urlpatterns

第三方面板也可以用于工具栏,可以(例如)报告缓存性能和模板呈现时间。 Third-party services

有一些免费服务奖从远程HTTP客户端的角度分析和报告您的网站的页面的性能,实际上模拟实际用户的体验。

这些不能报告您的代码的内部,但是可以提供有用的洞察您的网站的整体性能,包括不能从DJango环境中充分测量的方面。

实例包括:

雅虎Yslow

George pagespeed

还有一些付费服务执行类似的分析,包括一些是Django感知的,可以与您的代码库继承,以更广泛的分析其性能。

vieyahn2017 commented 4 years ago

7.line profiler

最好用的还是去line profiler去找程序的瓶颈,显示一个方法内哪行代码运行的时间最久。

8.在较低级别而不是较高的级别执行此工作计划总是更快。在更高层次,系统必须通过多级抽象和多层机械来处理对象。

也就是说,数据库通常可以比python更快的完成事情,这样比模板语言更快。

9.HTTP performance

Middleware

Django附带了一些有用的middleware,可以帮助优化您的网站的性能,他们包括

conditionGetMiddleware

添加了对现代浏览器的支持,可以根据ETag和Last-Modified标头有条件的获取响应。 GzipMiddleware

压缩所有现代浏览器的响应,节省带宽和传输时间,亲注意,GzipMiddleware目前被认为是一种安全风险,并且容易受到TSL/SSL提供的保护无效的攻击,有关详细的信息,请参阅GzipMiddleware中的警告。

10.Session

Using Cached session

Using Cached session可能是一种通过消除从像数据库这样比较慢的存储源加载的会话数据而改为经常使用的会话数据存储在内存中来提高性能的方法。

11.Static Files

静态文件,根据定义是不动态的,使优化增益的一个优秀的目标。

Minification

一些第三方Django工具和包提供了缩小HTML,css,javascript的能力,他们删除不必要的空格,换行符和注释,缩短变量名,从而减少您的网站发布文档的大小。

12.Template Performance

注意:

使用{% 阻止 %} 比使用{% 包括 %}

从许多小块的组装的重碎片模块可能会影响性能。

The cached template loader

启用cache temolate loader通常会大幅提高性能,因为它避免每次需要时编译每个模板渲染。

13.在代码方面的优化

核心模块使用cpython来实现,大幅度提高代码的运行速度。

vieyahn2017 commented 4 years ago

django 如何提升性能(高并发)

https://blog.csdn.net/weixin_42218868/article/details/99661112

对一个后端开发程序员来说,提升性能指标主要有两个一个是并发数,另一个是响应时间网站性能的优化一般包括 web 前端性能优化,应用服务器性能优化,存储服务器优化。 对前端的优化主要有: (1)减少 http 请求,减少数据库的访问量,比如使用雪碧图。 (2)使用浏览器缓存,将一些常用的 css,js,logo 图标,这些静态资源缓存到本地浏览器,通过设置 http 头中的 cache-control 和 expires 的属性,可设定浏览器缓存,缓存时间可以自定义。 (3)对 html,css,javascript 文件进行压缩,减少网络的通信量。

对我个人而言,我做的优化主要是以下三个方面: (1)合理的使用缓存技术,对一些常用到的动态数据,比如首页做一个缓存,或者某些常用的数据做个缓存,设置一定得过期时间,这样减少了对数据库的压力,提升网站性能。 (2)使用 celery 消息队列,将耗时的操作扔到队列里,让 worker 去监听队列里的任务,实现异步操作,比如发邮件,发短信。 (3)就是代码上的一些优化,补充:nginx 部署项目也是项目优化,可以配置合适的配置参数,提升效率,增加并发量。 (4)如果太多考虑安全因素,服务器磁盘用固态硬盘读写,远远大于机械硬盘,这个技术现在没有普及,主要是固态硬盘技术上还不是完全成熟, 相信以后会大量普及。 (5)另外还可以搭建服务器集群,将并发访问请求,分散到多台服务器上处理。 (6)最后就是运维工作人员的一些性能优化技术了。

vieyahn2017 commented 4 years ago

Python Django性能测试与优化指南

翻译csdn研发技术 最后发布于2017-12-12 15:43:35 阅读数 8033 收藏 https://blog.csdn.net/dev_csdn/article/details/78782570?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

原文:A Guide to Performance Testing and Optimization With Python and Django 作者:IULIAN GULEA 翻译:雁惊寒

摘要:本文通过一个简单的实例一步一步引导读者对其进行全方位的性能优化。以下是译文。

唐纳德·克努特(Donald Knuth)曾经说过:“不成熟的优化方案是万恶之源。”然而,任何一个承受高负载的成熟项目都不可避免地需要进行优化。在本文中,我想谈谈优化Web项目代码的五种常用方法。虽然本文是以Django为例,但其他框架和语言的优化原则也是类似的。通过使用这些优化方法,文中例程的查询响应时间从原来的77秒减少到了3.7秒。

Guide to Performance Optimization and Performance Testing With Python and Django 本文用到的例程是从一个我曾经使用过的真实项目改编而来的,是性能优化技巧的典范。如果你想自己尝试着进行优化,可以在GitHub上获取优化前的初始代码,并跟着下文做相应的修改。我使用的是Python 2,因为一些第三方软件包还不支持Python 3。

示例代码介绍 这个Web项目只是简单地跟踪每个地区的房产价格。因此,只有两种模型:

# houses/models.py
from utils.hash import Hasher

class HashableModel(models.Model):
    """Provide a hash property for models."""
    class Meta:
        abstract = True

    @property
    def hash(self):
        return Hasher.from_model(self)

class Country(HashableModel):
    """Represent a country in which the house is positioned."""
    name = models.CharField(max_length=30)

    def __unicode__(self):
        return self.name

class House(HashableModel):
    """Represent a house with its characteristics."""
    # Relations
    country = models.ForeignKey(Country, related_name='houses')

    # Attributes
    address = models.CharField(max_length=255)
    sq_meters = models.PositiveIntegerField()
    kitchen_sq_meters = models.PositiveSmallIntegerField()
    nr_bedrooms = models.PositiveSmallIntegerField()
    nr_bathrooms = models.PositiveSmallIntegerField()
    nr_floors = models.PositiveSmallIntegerField(default=1)
    year_built = models.PositiveIntegerField(null=True, blank=True)
    house_color_outside = models.CharField(max_length=20)
    distance_to_nearest_kindergarten = models.PositiveIntegerField(null=True, blank=True)
    distance_to_nearest_school = models.PositiveIntegerField(null=True, blank=True)
    distance_to_nearest_hospital = models.PositiveIntegerField(null=True, blank=True)
    has_cellar = models.BooleanField(default=False)
    has_pool = models.BooleanField(default=False)
    has_garage = models.BooleanField(default=False)
    price = models.PositiveIntegerField()

    def __unicode__(self):
        return '{} {}'.format(self.country, self.address)

抽象类HashableModel提供了一个继承自模型并包含hash属性的模型,这个属性包含了实例的主键和模型的内容类型。 这能够隐藏像实例ID这样的敏感数据,而用散列进行代替。如果项目中有多个模型,而且需要在一个集中的地方对模型进行解码并要对不同类的不同模型实例进行处理时,这可能会非常有用。 请注意,对于本文的这个小项目,即使不用散列也照样可以处理,但使用散列有助于展示一些优化技巧。

这是Hasher类:


# utils/hash.py
import basehash

class Hasher(object):
    @classmethod
    def from_model(cls, obj, klass=None):
        if obj.pk is None:
            return None
        return cls.make_hash(obj.pk, klass if klass is not None else obj)

    @classmethod
    def make_hash(cls, object_pk, klass):
        base36 = basehash.base36()
        content_type = ContentType.objects.get_for_model(klass, for_concrete_model=False)
        return base36.hash('%(contenttype_pk)03d%(object_pk)06d' % {
            'contenttype_pk': content_type.pk,
            'object_pk': object_pk
        })

    @classmethod
    def parse_hash(cls, obj_hash):
        base36 = basehash.base36()
        unhashed = '%09d' % base36.unhash(obj_hash)
        contenttype_pk = int(unhashed[:-6])
        object_pk = int(unhashed[-6:])
        return contenttype_pk, object_pk

    @classmethod
    def to_object_pk(cls, obj_hash):    
        return cls.parse_hash(obj_hash)[1]

由于我们想通过API来提供这些数据,所以我们安装了Django REST框架并定义以下序列化器和视图:

houses/serializers.py


class HouseSerializer(serializers.ModelSerializer):
    """Serialize a `houses.House` instance."""

    id = serializers.ReadOnlyField(source="hash")
    country = serializers.ReadOnlyField(source="country.hash")

    class Meta:
        model = House
        fields = (
            'id',
            'address',
            'country',
            'sq_meters',
            'price'
        )

houses/views.py


class HouseListAPIView(ListAPIView):
    model = House
    serializer_class = HouseSerializer
    country = None

    def get_queryset(self):
        country = get_object_or_404(Country, pk=self.country)
        queryset = self.model.objects.filter(country=country)
        return queryset

    def list(self, request, *args, **kwargs):
        # Skipping validation code for brevity
        country = self.request.GET.get("country")
        self.country = Hasher.to_object_pk(country)
        queryset = self.get_queryset()

        serializer = self.serializer_class(queryset, many=True)

        return Response(serializer.data)

现在,我们将用一些数据来填充数据库(使用factory-boy生成10万个房屋的实例:一个地区5万个,另一个4万个,第三个1万个),并准备测试应用程序的性能。

性能优化其实就是测量 在一个项目中我们需要测量下面这几个方面:

执行时间 代码的行数 函数调用次数 分配的内存 其他 但是,并不是所有这些都要用来度量项目的执行情况。一般来说,有两个指标比较重要:执行多长时间、需要多少内存。

在Web项目中,响应时间(服务器接收由某个用户的操作产生的请求,处理该请求并返回结果所需的总的时间)通常是最重要的指标,因为过长的响应时间会让用户厌倦等待,并切换到浏览器中的另一个选项卡页面。

在编程中,分析项目的性能被称为profiling。为了分析API的性能,我们将使用Silk包。在安装完这个包,并调用/api/v1/houses/?country=5T22RI后,可以得到如下的结果:

200 GET 
/api/v1/houses/

77292ms overall
15854ms on queries
50004 queries

整体响应时间为77秒,其中16秒用于查询数据库,总共有5万次查询。这几个数字很大,提升空间也有很大,所以,我们开始吧。

1. 优化数据库查询

性能优化最常见的技巧之一是对数据库查询进行优化,本案例也不例外。同时,还可以对查询做多次优化来减小响应时间。

1.1 一次提供所有数据

仔细看一下这5万次查询查的是什么:都是对houses_country表的查询:

200 GET 
/api/v1/houses/

77292ms overall
15854ms on queries
50004 queries

时间戳 表名  联合  执行时间(毫秒)
+0:01 :15.874374    “houses_country”    0   0.176
+0:01 :15.873304    “houses_country”    0   0.218
+0:01 :15.872225    “houses_country”    0   0.218
+0:01 :15.871155    “houses_country”    0   0.198
+0:01 :15.870099    “houses_country”    0   0.173
+0:01 :15.869050    “houses_country”    0   0.197
+0:01 :15.867877    “houses_country”    0   0.221
+0:01 :15.866807    “houses_country”    0   0.203
+0:01 :15.865646    “houses_country”    0   0.211
+0:01 :15.864562    “houses_country”    0   0.209
+0:01 :15.863511    “houses_country”    0   0.181
+0:01 :15.862435    “houses_country”    0   0.228
+0:01 :15.861413    “houses_country”    0   0.174

这个问题的根源是,Django中的查询是惰性的。这意味着在你真正需要获取数据之前它不会访问数据库。同时,它只获取你指定的数据,如果需要其他附加数据,则要另外发出请求。

这正是本例程所遇到的情况。当通过House.objects.filter(country=country)来获得查询集时,Django将获取特定地区的所有房屋。但是,在序列化一个house实例时,HouseSerializer需要房子的country实例来计算序列化器的country字段。由于地区数据不在查询集中,所以django需要提出额外的请求来获取这些数据。对于查询集中的每一个房子都是如此,因此,总共是五万次。

当然,解决方案非常简单。为了提取所有需要的序列化数据,你可以在查询集上使用select_related()。因此,get_queryset函数将如下所示:


def get_queryset(self):
    country = get_object_or_404(Country, pk=self.country)
    queryset = self.model.objects.filter(country=country).select_related('country')
    return queryset

我们来看看这对性能有何影响:


200 GET
/api/v1/houses/

35979ms overall
102ms on queries
4 queries

总体响应时间降至36秒,在数据库中花费的时间约为100ms,只有4个查询!这是个好消息,但我们可以做得更多。

1.2 仅提供相关的数据 默认情况下,Django会从数据库中提取所有字段。但是,当表有很多列很多行的时候,告诉Django提取哪些特定的字段就非常有意义了,这样就不会花时间去获取根本用不到的信息。在本案例中,我们只需要5个字段来进行序列化,虽然表中有17个字段。明确指定从数据库中提取哪些字段是很有意义的,可以进一步缩短响应时间。

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


def get_queryset(self):
    country = get_object_or_404(Country, pk=self.country)
    queryset = self.model.objects.filter(country=country)\
        .select_related('country')\
        .only('id', 'address', 'country', 'sq_meters', 'price')
    return queryset

这减少了一半的查询时间,非常不错。总体时间也略有下降,但还有更多提升空间。


200 GET
/api/v1/houses/

33111ms overall
52ms on queries
4 queries

2. 代码优化

你不能无限制地优化数据库查询,并且上面的结果也证明了这一点。即使把查询时间减少到0,我们仍然会面对需要等待半分钟才能得到应答这个现实。现在是时候转移到另一个优化级别上来了,那就是:业务逻辑。

2.1 简化代码 有时,第三方软件包对于简单的任务来说有着太大的开销。本文例程中返回的序列化的房子实例正说明了这一点。

Django REST框架非常棒,包含了很多有用的功能。但是,现在的主要目标是缩短响应时间,所以该框架是优化的候选对象,尤其是我们要使用的序列化对象这个功能非常的简单。

为此,我们来编写一个自定义的序列化器。为了方便起见,我们将用一个静态方法来完成这项工作。

houses/serializers.py

class HousePlainSerializer(object):
    """
    Serializes a House queryset consisting of dicts with
    the following keys: 'id', 'address', 'country',
    'sq_meters', 'price'.
    """

    @staticmethod
    def serialize_data(queryset):
        """
        Return a list of hashed objects from the given queryset.
        """
        return [
            {
                'id': Hasher.from_pk_and_class(entry['id'], House),
                'address': entry['address'],
                'country': Hasher.from_pk_and_class(entry['country'], Country),
                'sq_meters': entry['sq_meters'],
                'price': entry['price']
            } for entry in queryset
        ]

houses/views.py

class HouseListAPIView(ListAPIView):
    model = House
    serializer_class = HouseSerializer
    plain_serializer_class = HousePlainSerializer  # <-- added custom serializer
    country = None

    def get_queryset(self):
        country = get_object_or_404(Country, pk=self.country)
        queryset = self.model.objects.filter(country=country)
        return queryset

    def list(self, request, *args, **kwargs):
        # Skipping validation code for brevity
        country = self.request.GET.get("country")
        self.country = Hasher.to_object_pk(country)
        queryset = self.get_queryset()

        data = self.plain_serializer_class.serialize_data(queryset)  # <-- serialize

        return Response(data)

200 GET /api/v1/houses/

17312ms overall 38ms on queries 4 queries 1 2 3 4 5 6 现在看起来好多了,由于没有使用DRF序列化代码,所以响应时间几乎减少了一半。

另外还有一个结果:在请求/响应周期内完成的总的函数调用次数从15,859,427次(上面1.2节的请求次数)减少到了9,257,469次。这意味着大约有三分之一的函数调用都是由Django REST Framework产生的。

2.2 更新或替代第三方软件包 上述几个优化技巧是最常见的,无需深入地分析和思考就可以做到。然而,17秒的响应时间仍然感觉很长。要减少这个时间,需要更深入地了解代码,分析底层发生了什么。换句话说,需要分析一下代码。

你可以自己使用Python内置的分析器来进行分析,也可以使用一些第三方软件包。由于我们已经使用了silk,它可以分析代码并生成一个二进制的分析文件,因此,我们可以做进一步的可视化分析。有好几个可视化软件包可以将二进制文件转换为一些友好的可视化视图。本文将使用snakeviz。

这是上文一个请求的二进制分析文件的可视化图表:

Image of the view's dispatch method 从上到下是调用堆栈,显示了文件名、函数名及其行号,以及该方法花费的时间。可以很容易地看出,时间大部分都用在计算散列上(紫罗兰色的init.py和primes.py矩形)。

目前,这是代码的主要性能瓶颈,但同时,这不是我们自己写的代码,而是用的第三方包。

在这种情况下,我们可以做的事情将非常有限:

检查包的最新版本(希望能有更好的性能)。 寻找另一个能够满足我们需求的软件包。 我们自己写代码,并且性能优于目前使用的软件包。 幸运的是,我们找到了一个更新版本的basehash包。原代码使用的是v.2.1.0,而新的是v.3.0.4。

当查看v.3的发行说明时,这一句话看起来令人充满希望:

“使用素数算法进行大规模的优化。”

让我们来看一下!

pip install -U basehash gmpy2

200 GET
/api/v1/houses/

7738ms overall
59ms on queries
4 queries

响应时间从17秒缩短到了8秒以内。太棒了!但还有一件事我们应该来看看。

2.3 重构代码 到目前为止,我们已经改进了查询、用自己特定的函数取代了第三方复杂而又泛型的代码、更新了第三方包,但是我们还是保留了原有的代码。但有时,对现有代码进行小规模的重构可能会带来意想不到的结果。但是,为此我们需要再次分析运行结果。

Image of profiling results 仔细看一下,你可以看到散列仍然是一个问题(毫不奇怪,这是我们对数据做的唯一的事情),虽然我们确实朝这个方向改进了,但这个绿色的矩形表示init.py花了2.14秒的时间,同时伴随着灰色的init.py:54(hash)。这意味着初始化工作需要很长的时间。

我们来看看basehash包的源代码。

# basehash/__init__.py

# Initialization of `base36` class initializes the parent, `base` class.
class base36(base):
    def __init__(self, length=HASH_LENGTH, generator=GENERATOR):
        super(base36, self).__init__(BASE36, length, generator)

class base(object):
    def __init__(self, alphabet, length=HASH_LENGTH, generator=GENERATOR):
        if len(set(alphabet)) != len(alphabet):
            raise ValueError('Supplied alphabet cannot contain duplicates.')

        self.alphabet = tuple(alphabet)
        self.base = len(alphabet)
        self.length = length
        self.generator = generator
        self.maximum = self.base ** self.length - 1
        self.prime = next_prime(int((self.maximum + 1) * self.generator))  # `next_prime` call on each initialized instance

正如你所看到的,一个base实例的初始化需要调用next_prime函数,这是太重了,我们可以在上面的可视化图表中看到左下角的矩形。

我们再来看看Hash类:

class Hasher(object):
    @classmethod
    def from_model(cls, obj, klass=None):
        if obj.pk is None:
            return None
        return cls.make_hash(obj.pk, klass if klass is not None else obj)

    @classmethod
    def make_hash(cls, object_pk, klass):
        base36 = basehash.base36()  # <-- initializing on each method call
        content_type = ContentType.objects.get_for_model(klass, for_concrete_model=False)
        return base36.hash('%(contenttype_pk)03d%(object_pk)06d' % {
            'contenttype_pk': content_type.pk,
            'object_pk': object_pk
        })

    @classmethod
    def parse_hash(cls, obj_hash):
        base36 = basehash.base36()  # <-- initializing on each method call
        unhashed = '%09d' % base36.unhash(obj_hash)
        contenttype_pk = int(unhashed[:-6])
        object_pk = int(unhashed[-6:])
        return contenttype_pk, object_pk

    @classmethod
    def to_object_pk(cls, obj_hash):    
        return cls.parse_hash(obj_hash)[1]

正如你所看到的,我已经标记了这两个方法初始化base36实例的方法,这并不是真正需要的。

由于散列是一个确定性的过程,这意味着对于一个给定的输入值,它必须始终生成相同的散列值,因此,我们可以把它作为类的一个属性。让我们来看看它将如何执行:

class Hasher(object):
    base36 = basehash.base36()  # <-- initialize hasher only once

    @classmethod
    def from_model(cls, obj, klass=None):
        if obj.pk is None:
            return None
        return cls.make_hash(obj.pk, klass if klass is not None else obj)

    @classmethod
    def make_hash(cls, object_pk, klass):
        content_type = ContentType.objects.get_for_model(klass, for_concrete_model=False)
        return cls.base36.hash('%(contenttype_pk)03d%(object_pk)06d' % {
            'contenttype_pk': content_type.pk,
            'object_pk': object_pk
        })

    @classmethod
    def parse_hash(cls, obj_hash):
        unhashed = '%09d' % cls.base36.unhash(obj_hash)
        contenttype_pk = int(unhashed[:-6])
        object_pk = int(unhashed[-6:])
        return contenttype_pk, object_pk

    @classmethod
    def to_object_pk(cls, obj_hash):    
        return cls.parse_hash(obj_hash)[1]

再次测试 200 GET

/api/v1/houses/

3766ms overall 38ms on queries 4 queries

最后的结果是在4秒钟之内,比我们一开始的时间要小得多。对响应时间的进一步优化可以通过使用缓存来实现,但是我不会在这篇文章中介绍这个。

结论

性能优化是一个分析和发现的过程。 没有哪个硬性规定能适用于所有情况,因为每个项目都有自己的流程和瓶颈。 然而,你应该做的第一件事是分析代码。 如果在这样一个简短的例子中,我可以将响应时间从77秒缩短到3.7秒,那么对于一个庞大的项目来说,就会有更大的优化潜力。

vieyahn2017 commented 4 years ago

Django 性能测试——一个现实世界的例子

https://blog.csdn.net/youmypig/article/details/8161809

vieyahn2017 commented 4 years ago

简单4步,提高django性能

https://blog.csdn.net/u011546806/article/details/45576669

翻译thomas-23 最后发布于2015-05-08 10:40:53 阅读数 5963 收藏

性能优化是一件困难的事情,但是也不常常如此: 下面4步将能够轻松的提高你的网站的性能,它们非常简单你应该将它们 作为标配。

持久化数据库连接

django1.6以后已经内置了数据库持久化连接,很多人使用PostgreSQL作为它们的线上数据库系统,而当我们连接PostgreSQL有时会显得很慢,这里我们可以进行优化。 没有持久化连接,每一个网站的请求都会与数据库建立一个连接。如果数据库不在本地,尽管网速很快,这也将花费20-75ms.

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

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

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

模版加载

默认django使用两个标准的模版加载器

TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

每一个请求,这些模版加载器都会去文件系统搜索,然后解析这些模版。 这里可以感觉到,它是不是可以处理的更快了? 你可以开启缓存加载,因此django只会查找并且解析你的模版一次 配置如下:

TEMPLATE_LOADERS = (
    ('django.template.loaders.cached.Loader', (
        'django.template.loaders.filesystem.Loader',
        'django.template.loaders.app_directories.Loader',
    )),
)

但是,不要在开发环境中开启缓存加载,这样会很烦人的,因为每次模版做了修改之后你都需要重启服务才能看到修改后的效果

优化django会话

很多人不知道django的session的存储,django存储sessions在数据库中,并希望有时候能够清理掉一些不用了的,但是很少人会这么做。 这样的话每一个请求都要使用sql查询会话数据,然后获得用户对象的信息。 一些对性能比较关心的人,会使用memory cache或者redis来缓存一些东西,这样的话,你可以将你的会话放到缓存数据库中,因此轻松移除掉每一次请求都会去进行sql查询的所耗费的时间。 设置如下:

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

前面假设你可以丢失任何存储的会话,如果不是那样的话,像下面这样配置:

SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'’'

这里,后台会缓存数据,并同时会把修改的内容写入到数据库中长期保存

select_related() 和 prefetch_related()

相比于修改数据库存储,这里只要需要简单的设置select_related()和prefetch_related(),在使用ORM的情况下,他能够减少sql查询的数量。 这里有一个BlogPost模型,它有一个用户外键,获得了一个listview

queryset = BlogPost.objects.active

那么在模版中是这样使用:

<ul>
{% for post in object_list %}
  <li>{{ post.title }} - {{ post.user.email }}</li>
{% endfor %}
</ul>

这里满足了预期的效果,但是每个post都会去查询auth_user表。为了解决这个问题,可以让djangode的ORM在前面就连接上auth_user 表,这样object.user就是一个可以直接用的对象了,这样BlogPost.objects.active().count()就会变成一个简单的查询

修改如下:

queryset = BlogPost.objects.select_related().active()

prefetch_related的机理是相同的

当感到疑惑时,开启django调试工具,然后分析每次请求的查询次数和时间,如果每次查询都要用上5~10次,那么这些就是可以优化的线索

vieyahn2017 commented 4 years ago

Django开发中数据库优化的方法 原创myq151 最后发布于2018-12-22 15:28:37 阅读数 521 收藏

在软件开发过程中,数据库的存在是必须的。所谓数据库就是数据存储的仓库,用于存储数据的。在开发过程中,数据库需要涉及到一些优化,以便提高数据库的性能,提高系统的性能。

以下是本人总结的数据库优化方法: 1.设计表时,尽量少使用外键,因为外键约束会影响插入和删除的性能; 2.使用缓存,减少对数据库的访问; 3.在 orm 框架下设计表时,能用 varchar 确定字段长度就不用用text; 4.在定义时,可以给搜索频率高的字段属性创建索引; 5.Django orm 框架下的Querysets 本来就有缓存的功能; 6.如果一个页面只需要数据库中某一字段时,可以用QuerySet,values(); 7.如果一个页面需要多次连接数据库,可以一次性取出所有需要的数据,减少对数据库的查询次数; 8.在模板标签里使用with 标签可以缓存Qset 的查询结果。 ———————————————— 版权声明:本文为CSDN博主「myq151」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/myq151/article/details/85208493

vieyahn2017 commented 4 years ago

查找 Django 项目中的性能瓶颈 https://blog.csdn.net/enlangs/article/details/85293100

vieyahn2017 commented 4 years ago

django性能监测工具silk使用方法(初级) 原创老王不需要昵称 最后发布于2018-08-09 14:02:58 阅读数 2421 收藏 展开 django-silk介绍 web开发过程中,经常要掌握程序的性能状况,遇到性能瓶颈还要进行进一步的分析,如执行时间、网络耗时、数据库连接时间等;如果你是基于django框架开发,那接下来,那么django-silk将会成为你的得力助手。

环境需求 Django: 1.11, 2.0 Python: 2.7, 3.4, 3.5, 3.6 安装 django-silk地址: https://github.com/jazzband/django-silk

pip install -U django-silk

———————————————— 版权声明:本文为CSDN博主「老王不需要昵称」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_37590093/article/details/81535539